Compare commits

...

10 Commits

27 changed files with 1784 additions and 1318 deletions

View File

@ -1,10 +1,44 @@
<!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> <meta
name="description"
content="Fakebook social media app, which reminds you Facebook and contains AI generated code to use AINIRO Magic backend"
/>
<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"
} }
} }

19
prompts/commit30.txt Normal file
View File

@ -0,0 +1,19 @@
Do you still remember exactly the backend.js file?
Now I want you to clean it up a bit.
1.I only want to see one line comments
2.You only read the local storage in one function, which
updates the global variables you use for the (current) user
id and the token for the authorization of the requests
3.Let's keep everything working just unify the code style
a bit for better readability
Thank you
Can I get the whole file?
The rest of the code please?
You forgot to provide the password reminder function for the
signatures.

6
prompts/commit32.txt Normal file
View File

@ -0,0 +1,6 @@
We are going to make another feature, which is the password
reminder email. We need to write the function, which will send a
request to the GET pswreminder endpoint. It has only one
parameter, which is the email. The user gets the email with a
link, which is valid for an hour and takes them to a site to
change the password.

10
prompts/commit34.txt Normal file
View File

@ -0,0 +1,10 @@
The last fix before deployment is the online status, when someone
closes the browser window, without signing out. Although we
handle the event which is visibility change to send the request,
the browser closes before the request is sent. Fix this in the
backend.js file and remove the unnecessary before unload listener
from the UserAccount component.
How about if someone just hides the browser window on mobile for
example?

13
prompts/commit36.txt Normal file
View File

@ -0,0 +1,13 @@
Audio description:
"How can I deploy my previous project on GitHub pages? Take into
account that it's a single page application and GitHub pages is
not the best for deploying these or you need to use some kind of
tool for React apps. Let me the detailed answer, please."
What are the differences for Vite build?
// The deployment worked at this point, but the AI (o3) eliminated
// the usage of the isFriendsListPage field from the accountPage
// slice in the Redux store. This field determines in the UI if it
// shows the Profile component or the FriendsListPage component.
// I manually debugged the UserAccount component.

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]
);
useEffect(() => dispatch(profileLinkSet(profileLink)), [profileLink, dispatch]); //handling the password reminder button
const [isModalSignup, setModalSignup] = useState(true);
//handling the password reminder button function handleClickPasswordReminderBtn() {
const [isModalSignup, setModalSignup] = useState(true); setModalSignup(false);
handleShow();
}
function handleClickPasswordReminderBtn() { if (user.isLoading) {
setModalSignup(false); return <div>...Loading</div>;
handleShow(); }
}
if (user.isLoading) { if (user.isSignedIn && !user.error) {
return <div>...Loading</div>; 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) { <hr />
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 /> <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 <SignupModal
variant="success" show={show && isModalSignup}
size="lg" onHide={handleCloseCallback}
className="d-block w-60 mx-auto mt-4" onExit={() => setModalSignup(true)}
onClick={handleShow}> ></SignupModal>
<b>Create New Account</b>
</Button>
</div>
</Col>
<SignupModal <PasswordReminderModal
show={show && isModalSignup} show={show && !isModalSignup}
onHide={handleCloseCallback} onHide={handleClose}
onExit={() => setModalSignup(true)}></SignupModal> onExit={() => setModalSignup(true)}
/>
<PasswordReminderModal </Row>
show={show && !isModalSignup} </Col>
onHide={handleClose} );
onExit={() => setModalSignup(true)} }
/>
</Row>
</Col>
);
}
} }
export default App; export default App;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
import React from "react";
// For Bootstrap 4: use "sr-only" for visually hidden text
const CustomToggle = React.forwardRef(({ onClick }, ref) => (
<button
ref={ref}
className='btn dropdown-toggle custom-drop-down-btn'
aria-label='Account menu'
data-toggle='dropdown'
aria-haspopup='true'
aria-expanded='false'
type='button'
onClick={(e) => {
e.preventDefault();
onClick(e);
}}
>
<span className='sr-only'>Account menu</span>
{/* Bootstrap's caret will appear via .dropdown-toggle CSS */}
</button>
));
export default CustomToggle;

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

@ -51,13 +51,17 @@
width: 100%; width: 100%;
border: 2px solid lightgray; border: 2px solid lightgray;
border-radius: 10px; border-radius: 10px;
padding: 10px; overflow: hidden;
background: #000;
position: relative; position: relative;
padding-bottom: 56.25%; /* 16:9, for an aspect ratio of 1:1 change to this value to 100% */ padding-bottom: 56.25%;
height: 0;
} }
.react-player { .react-player {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
width: 100% !important;
height: 100% !important;
} }

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

@ -18,14 +18,14 @@ const ProfileLink = (props) => {
color: "inherited", color: "inherited",
}} }}
> >
<Col xs="auto" className="px-2 ml-2"> <Col xs='auto' className='px-2 ml-2'>
<CircularImage <CircularImage
size={size} size={size}
url={profilePictureURL} url={profilePictureURL}
isOnline={isOnline} isOnline={isOnline}
/> />
</Col> </Col>
<Col className="align-self-center p-0" style={{ color: "inherited" }}> <Col className='align-self-center p-0 text-dark'>
{bold === "true" ? <b>{name}</b> : name} {bold === "true" ? <b>{name}</b> : name}
</Col> </Col>
</Row> </Row>

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

@ -45,6 +45,37 @@
left: 0; left: 0;
} }
.custom-drop-down-btn .dropdown-toggle {
display: flex;
align-items: center;
justify-content: center;
width: 1.7em; /* as in your original custom-drop-down-btn styling */
height: 1.7em;
padding: 0 !important; /* Remove Bootstrap padding */
background: #e9ecef !important;
border-radius: 1.7em;
border: 1px solid #e9ecef !important;
box-shadow: none;
/* You can adjust height/width for your visual design */
}
/* Target the caret in the dropdown toggle and move it left and down */
.custom-drop-down-btn .dropdown-toggle::after {
margin-left: 0 !important; /* Remove any left margin */
transform: translate(0, 2px); /* Move caret left (0px) and down (+2px) */
/* Try larger/smaller values if needed */
}
/* If you want to be very precise... */
.custom-drop-down-btn .dropdown-toggle {
display: flex;
align-items: center;
justify-content: center;
padding: 0 !important;
}
@media (max-width: 640px) { @media (max-width: 640px) {
.spaceing, .spaceing,
.first { .first {

View File

@ -1,5 +1,5 @@
import React, { useRef } from "react"; import React, { useRef } from "react";
import { Navbar, Nav, Dropdown, DropdownButton } from "react-bootstrap"; import { Navbar, Nav, Dropdown } from "react-bootstrap";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { VscHome } from "react-icons/vsc"; import { VscHome } from "react-icons/vsc";
import { FaFacebook } from "react-icons/fa"; import { FaFacebook } from "react-icons/fa";
@ -14,124 +14,138 @@ import { useEffect } from "react";
import { handleClickLink } from "./helper"; 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";
import CustomToggle from "./CustomToggle";
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='/' aria-label='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='/' className='nav-link' ref={refs.home} aria-label='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}
aria-label='friends list'
>
<FaUserFriends
fontSize='2rem'
className='mx-4'
style={{ pointerEvents: "none" }}
/>
</Link>
</Nav.Item>
<Nav.Item>
<Link
to='/watch'
className='nav-link'
ref={refs.watch}
aria-label='videos'
>
<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'
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'>
<Dropdown className='mr-4 custom-drop-down-btn'>
<Dropdown.Toggle as={CustomToggle} />
return ( <Dropdown.Menu alignRight>
<div className="titlebar bg-light"> <Dropdown.Item
<Navbar bg="light" className="p-0 nav-container"> as={Link}
<Navbar.Brand as={Link} to="/fakebook"> to={profileLink}
<FaFacebook color="dodgerblue" fontSize="2em" className="mx-3" /> onClick={closeFriendsListPage}
</Navbar.Brand> >
<div style={{ width: "450px" }} className="spaceing" /> <ProfileLink
<Nav className="w-75 justify-content-start mr-5"> user={user}
<Nav.Item className="first"> size='60'
<Link to="/fakebook" className="nav-link" ref={refs.home}> fullname='true'
<VscHome bold='true'
fontSize="2rem" />
className="mx-4" </Dropdown.Item>
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"> <Dropdown.Divider />
<Nav.Item className="align-self-center first">
<Link <Dropdown.Item
to={profileLink} as={Link}
className="nav-link profile" to='/'
id="profile" onClick={handleClick}
onClick={closeFriendsListPage} className='p-0'
ref={refs.profile}> >
<ProfileLink user={user} size="30" fullname="false" bold="true" /> <ImExit fontSize='1.5em' className='mx-4' />
</Link>
</Nav.Item> <span>Log Out</span>
<Nav.Item className="align-self-center">
<DropdownButton <div style={{ width: "20em" }}></div>
title="" </Dropdown.Item>
className="mr-4 custom-drop-down-btn" </Dropdown.Menu>
menuAlign="right"> </Dropdown>
<Dropdown.Item </Nav.Item>
as={Link} </Nav>
to={profileLink} </Navbar>
onClick={closeFriendsListPage}> </div>
<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

@ -38,7 +38,6 @@ import {
/* Mock-backend helpers */ /* Mock-backend helpers */
import { import {
currentUserOffline,
currentUserOnline, currentUserOnline,
subscribeCurrentUser, subscribeCurrentUser,
subscribeUsers, subscribeUsers,
@ -56,16 +55,24 @@ const RouteStateSync = () => {
const location = useLocation(); const location = useLocation();
const isFriendsListPage = useSelector(
(state) => state.accountPage.isFriendsListPage
);
useEffect(() => { useEffect(() => {
const { pathname } = location; const { pathname } = location;
/* friends list page */ /* friends list page */
dispatch(friendsListPageSet(pathname.startsWith("/fakebook/friends/list"))); dispatch(
friendsListPageSet(
pathname.startsWith("/friends/list") || isFriendsListPage
)
);
/* watch page (videos feed) */ /* watch page (videos feed) */
dispatch(watchSet(pathname.startsWith("/fakebook/watch"))); dispatch(watchSet(pathname.startsWith("/watch")));
}, [location, dispatch]); }, [location, dispatch]);
return null; // renders nothing return null; // renders nothing
@ -84,9 +91,13 @@ const UserAccount = () => {
const users = useSelector((state) => state.users); const users = useSelector((state) => state.users);
const isFriendsListPage = useSelector(
(state) => state.accountPage.isFriendsListPage
);
/* -------------------------------------------------- */ /* -------------------------------------------------- */
/* Firestore-like subscriptions & online/offline flag */ /* Firestore-like subscriptions */
/* -------------------------------------------------- */ /* -------------------------------------------------- */
@ -101,21 +112,6 @@ const UserAccount = () => {
currentUserOnline(); currentUserOnline();
/* window closed or refreshed */
const beforeUnload = () => currentUserOffline();
window.addEventListener("beforeunload", beforeUnload);
/* tab visibility switch */
const visChange = () =>
document.visibilityState === "visible"
? currentUserOnline()
: currentUserOffline();
document.addEventListener("visibilitychange", visChange);
/* cleanup */ /* cleanup */
return () => { return () => {
@ -124,10 +120,6 @@ const UserAccount = () => {
unsubUsers(); unsubUsers();
unsubPosts(); unsubPosts();
window.removeEventListener("beforeunload", beforeUnload);
document.removeEventListener("visibilitychange", visChange);
}; };
}, []); }, []);
@ -167,7 +159,7 @@ const UserAccount = () => {
return ( return (
<div className='bg-200 vw-100 main-container overflow-hidden'> <div className='bg-200 vw-100 main-container overflow-hidden'>
<Container className='w-100 p-0' fluid> <Container className='w-100 p-0' fluid>
<Router> <Router basename='/fakebook-aigen'>
<RouteStateSync /> <RouteStateSync />
<TitleBar /> <TitleBar />
@ -175,27 +167,30 @@ const UserAccount = () => {
<Switch> <Switch>
{/* Friends list ------------------------------------------------ */} {/* Friends list ------------------------------------------------ */}
<Route path='/fakebook/friends/list' component={FriendsListPage} /> <Route path='/friends/list' component={FriendsListPage} />
{/* Single photo ----------------------------------------------- */} {/* Single photo ----------------------------------------------- */}
<Route path='/fakebook/photo/:userID/:n' component={PhotoViewer} /> <Route path='/photo/:userID/:n' component={PhotoViewer} />
{/* Watch (video feed) ----------------------------------------- */} {/* Watch (video feed) ----------------------------------------- */}
<Route <Route
path='/fakebook/watch' path='/watch'
render={(props) => <HomePage {...props} className='pt-5' />} render={(props) => <HomePage {...props} className='pt-5' />}
/> />
{/* User profile ----------------------------------------------- */} {/* User profile ----------------------------------------------- */}
<Route path='/fakebook/:userName' component={Profile} /> <Route
path='/:userName'
component={isFriendsListPage ? FriendsListPage : Profile}
/>
{/* News-feed root --------------------------------------------- */} {/* News-feed root --------------------------------------------- */}
<Route <Route
path='/fakebook' path='/'
exact exact
render={(props) => <HomePage {...props} className='pt-5' />} render={(props) => <HomePage {...props} className='pt-5' />}
/> />

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
});