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"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <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> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

616
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "fakebook-ainiro", "name": "fakebook-ainiro",
"version": "0.0.0", "version": "0.1.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "fakebook-ainiro", "name": "fakebook-ainiro",
"version": "0.0.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@microsoft/signalr": "^8.0.7", "@microsoft/signalr": "^8.0.7",
"@reduxjs/toolkit": "^1.8.3", "@reduxjs/toolkit": "^1.8.3",
@ -17,11 +17,11 @@
"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",
"gh-pages": "^6.3.0",
"vite": "^6.0.5" "vite": "^6.0.5"
} }
}, },
@ -847,6 +847,44 @@
"ws": "^7.4.5" "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": { "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",
@ -1333,6 +1371,23 @@
"node": ">=6.5" "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": { "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",
@ -1370,6 +1425,19 @@
"popper.js": "^1.16.1" "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": { "node_modules/browserslist": {
"version": "4.24.4", "version": "4.24.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
@ -1430,6 +1498,23 @@
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
"license": "MIT" "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": { "node_modules/convert-source-map": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@ -1509,6 +1594,19 @@
"node": ">=6" "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": { "node_modules/dom-helpers": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
@ -1585,6 +1683,13 @@
"dev": true, "dev": true,
"license": "ISC" "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": { "node_modules/entities": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
@ -1649,6 +1754,16 @@
"node": ">=6" "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": { "node_modules/estree-walker": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
@ -1674,6 +1789,33 @@
"node": ">=12.0.0" "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": { "node_modules/fetch-cookie": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-2.2.0.tgz", "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-2.2.0.tgz",
@ -1684,6 +1826,104 @@
"tough-cookie": "^4.0.0" "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": { "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",
@ -1709,6 +1949,42 @@
"node": ">=6.9.0" "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": { "node_modules/globals": {
"version": "11.12.0", "version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@ -1719,6 +1995,34 @@
"node": ">=4" "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": { "node_modules/he": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@ -1752,6 +2056,16 @@
"react-is": "^16.7.0" "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": { "node_modules/immer": {
"version": "9.0.21", "version": "9.0.21",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
@ -1771,6 +2085,39 @@
"loose-envify": "^1.0.0" "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": { "node_modules/isarray": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
@ -1781,7 +2128,8 @@
"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",
@ -1815,6 +2163,29 @@
"node": ">=6" "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": { "node_modules/kolorist": {
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz",
@ -1828,6 +2199,19 @@
"integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==", "integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==",
"license": "MIT" "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": { "node_modules/loose-envify": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@ -1863,12 +2247,52 @@
"node": ">=12" "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": { "node_modules/memoize-one": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
"license": "MIT" "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": { "node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -1955,6 +2379,55 @@
"node": ">=0.10.0" "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": { "node_modules/path-to-regexp": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz",
@ -1964,6 +2437,16 @@
"isarray": "0.0.1" "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": { "node_modules/picocolors": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@ -1984,6 +2467,19 @@
"url": "https://github.com/sponsors/jonschlinkert" "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": { "node_modules/popper.js": {
"version": "1.16.1", "version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
@ -2086,6 +2582,27 @@
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
"license": "MIT" "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": { "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",
@ -2347,6 +2864,17 @@
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==", "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==",
"license": "MIT" "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": { "node_modules/rollup": {
"version": "4.30.1", "version": "4.30.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.1.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.1.tgz",
@ -2386,6 +2914,30 @@
"fsevents": "~2.3.2" "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": { "node_modules/scheduler": {
"version": "0.20.2", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
@ -2412,13 +2964,14 @@
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/signalr": { "node_modules/slash": {
"version": "2.4.3", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/signalr/-/signalr-2.4.3.tgz", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-RbBKFVCZvDgyyxZDeu6Yck9T+diZO07GB0bDiKondUhBY1H8JRQSOq8R0pLkf47ddllQAssYlp7ckQAeom24mw==", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"license": "Apache-2.0", "dev": true,
"dependencies": { "license": "MIT",
"jquery": ">=1.6.4" "engines": {
"node": ">=8"
} }
}, },
"node_modules/source-map": { "node_modules/source-map": {
@ -2451,6 +3004,19 @@
"node": ">=16" "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": { "node_modules/tiny-invariant": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
@ -2463,6 +3029,19 @@
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
"license": "MIT" "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": { "node_modules/tough-cookie": {
"version": "4.1.4", "version": "4.1.4",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
@ -2484,6 +3063,19 @@
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"license": "MIT" "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": { "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",

View File

@ -1,12 +1,15 @@
{ {
"name": "fakebook-ainiro", "name": "fakebook-ainiro",
"private": true, "private": true,
"version": "0.0.0", "version": "0.1.0",
"homepage": "https://alexerdei73.github.io/fakebook-aigen",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"preview": "vite preview" "preview": "vite preview",
"predeploy": "npm run build",
"deploy": "gh-pages -d dist"
}, },
"dependencies": { "dependencies": {
"@microsoft/signalr": "^8.0.7", "@microsoft/signalr": "^8.0.7",
@ -18,11 +21,11 @@
"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",
"gh-pages": "^6.3.0",
"vite": "^6.0.5" "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"; import { profileLinkSet } from "./features/accountPage/accountPageSlice";
function App() { function App() {
const user = useSelector((state) => state.user); const user = useSelector((state) => state.user);
const dispatch = useDispatch(); const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
const unsubscribe = subscribeAuth(); const unsubscribe = subscribeAuth();
return unsubscribe; return unsubscribe;
}, []); }, []);
//Handle the modal //Handle the modal
const [show, setShow] = useState(false); const [show, setShow] = useState(false);
function handleClose() { function handleClose() {
setShow(false); setShow(false);
} }
function handleShow() { function handleShow() {
setShow(true); setShow(true);
} }
const handleCloseCallback = useCallback(handleClose, []); const handleCloseCallback = useCallback(handleClose, []);
//get the first and lastName for the route of the profile //get the first and lastName for the route of the profile
const name = const name =
(user && user.displayName && user.displayName.trim().split(" ")) || []; (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]);
//handling the password reminder button useEffect(
const [isModalSignup, setModalSignup] = useState(true); () => dispatch(profileLinkSet(profileLink)),
[profileLink, dispatch]
);
function handleClickPasswordReminderBtn() { //handling the password reminder button
setModalSignup(false); const [isModalSignup, setModalSignup] = useState(true);
handleShow();
}
if (user.isLoading) { function handleClickPasswordReminderBtn() {
return <div>...Loading</div>; setModalSignup(false);
} handleShow();
}
if (user.isSignedIn && !user.error) { if (user.isLoading) {
if (user.isEmailVerified) return <UserAccount />; return <div>...Loading</div>;
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 /> 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>
<Button <hr />
variant="success"
size="lg"
className="d-block w-60 mx-auto mt-4"
onClick={handleShow}>
<b>Create New Account</b>
</Button>
</div>
</Col>
<SignupModal <Button
show={show && isModalSignup} variant='success'
onHide={handleCloseCallback} size='lg'
onExit={() => setModalSignup(true)}></SignupModal> className='d-block w-60 mx-auto mt-4'
onClick={handleShow}
>
<b>Create New Account</b>
</Button>
</div>
</Col>
<PasswordReminderModal <SignupModal
show={show && !isModalSignup} show={show && isModalSignup}
onHide={handleClose} onHide={handleCloseCallback}
onExit={() => setModalSignup(true)} onExit={() => setModalSignup(true)}
/> ></SignupModal>
</Row>
</Col> <PasswordReminderModal
); show={show && !isModalSignup}
} onHide={handleClose}
onExit={() => setModalSignup(true)}
/>
</Row>
</Col>
);
}
} }
export default App; export default App;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -15,10 +15,10 @@ import FriendsListPage from "./FriendsListPage";
/* Router */ /* Router */
import { import {
BrowserRouter as Router, BrowserRouter as Router,
Switch, Switch,
Route, Route,
useLocation, useLocation,
} from "react-router-dom"; } from "react-router-dom";
/* Layout */ /* Layout */
@ -30,19 +30,18 @@ import Container from "react-bootstrap/Container";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { import {
friendsListPageSet, friendsListPageSet,
profileLinkSet, profileLinkSet,
watchSet, watchSet,
} from "../features/accountPage/accountPageSlice"; } from "../features/accountPage/accountPageSlice";
/* Mock-backend helpers */ /* Mock-backend helpers */
import { import {
currentUserOffline, currentUserOnline,
currentUserOnline, subscribeCurrentUser,
subscribeCurrentUser, subscribeUsers,
subscribeUsers, subscribePosts,
subscribePosts,
} from "../backend/backend"; } from "../backend/backend";
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
@ -52,139 +51,154 @@ import {
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
const RouteStateSync = () => { const RouteStateSync = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const location = useLocation(); const location = useLocation();
useEffect(() => { const isFriendsListPage = useSelector(
const { pathname } = location; (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"))); /* watch page (videos feed) */
}, [location, dispatch]);
return null; // renders nothing dispatch(watchSet(pathname.startsWith("/watch")));
}, [location, dispatch]);
return null; // renders nothing
}; };
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
const UserAccount = () => { 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 () => { /* cleanup */
unsubCurrentUser();
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 = const base = profileLink.replace(/\.\d+$/, "");
currentUser.index && currentUser.index > 0
? `${base}.${currentUser.index}`
: base;
dispatch(profileLinkSet(newLink)); const newLink =
}, [currentUser, profileLink, dispatch]); currentUser.index && currentUser.index > 0
? `${base}.${currentUser.index}`
: base;
/* Loading guard */ dispatch(profileLinkSet(newLink));
}, [currentUser, profileLink, dispatch]);
if (!currentUser || users.length === 0) { /* Loading guard */
return <div>Loading</div>;
}
/* -------------------------------------------------- */ 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> <TitleBar />
{/* Friends list ------------------------------------------------ */}
<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 {/* Watch (video feed) ----------------------------------------- */}
path="/fakebook/watch"
render={(props) => <HomePage {...props} className="pt-5" />}
/>
{/* 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 {/* News-feed root --------------------------------------------- */}
path="/fakebook"
exact <Route
render={(props) => <HomePage {...props} className="pt-5" />} path='/'
/> exact
</Switch> render={(props) => <HomePage {...props} className='pt-5' />}
</Router> />
</Container> </Switch>
</div> </Router>
); </Container>
</div>
);
}; };
export default UserAccount; export default UserAccount;

View File

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