diff --git a/backend/app.js b/backend/app.js index 563316a..ecc466e 100644 --- a/backend/app.js +++ b/backend/app.js @@ -18,6 +18,10 @@ var ProfileRoutes = require("./routes/profile") var PostRoutes = require("./routes/post") var HomeRoutes = require("./routes/home") + +var cors = require('cors'); +app.use(cors()); + // sets up the MongoDB var url = "mongodb://localhost/melody" // var url = "mongodb+srv://public:0vRokIdC25tC532f@melody.1dhd4.mongodb.net/Melody?retryWrites=true&w=majority" @@ -76,7 +80,7 @@ app.use(PostRoutes) // renders the home page with posts in chronological order if random url is entered app.get("/*", function(req, res) { // res.redirect("home") - res.send('hello'); + res.send('what it do baby'); }) diff --git a/backend/package-lock.json b/backend/package-lock.json index 41462da..b941e8a 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -369,6 +369,15 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", diff --git a/backend/package.json b/backend/package.json index 5f010f9..706e881 100644 --- a/backend/package.json +++ b/backend/package.json @@ -13,6 +13,7 @@ "bcrypt": "^5.0.0", "body-parser": "^1.19.0", "connect-mongodb-session": "^2.3.3", + "cors": "^2.8.5", "ejs": "^3.1.3", "express": "^4.17.1", "express-session": "^1.17.1", diff --git a/backend/routes/home.js b/backend/routes/home.js index 5536535..e408030 100644 --- a/backend/routes/home.js +++ b/backend/routes/home.js @@ -8,11 +8,11 @@ var middleware = require("../middleware/index") // renders the home page with posts in chronological order router.get("/home", middleware.isLoggedIn, function(req, res) { User.findById(req.user._id, function(err, user) { - if (err) return next(err); + if (err) return res.status(400).send(err) var following = user.following.map(item => item.username) following.push(user.username) Post.find({"author.username": {$in : following}}, function(err, posts) { - if (err) console.log(err); + if (err) return res.status(400).send(err) var songs = 0; var popular = posts.map(post => post.songTitle) var popularFive = [] @@ -28,36 +28,36 @@ router.get("/home", middleware.isLoggedIn, function(req, res) { } songs ++ } - res.render("home", {posts: posts, popular: popularFive}) + return res.status(200).send({posts: posts, popular: popularFive}) }) }) }) -// searches for users by username, and sends the list -router.post('/search', async function(req, res, next) { - var text = req.body.text - User.find({'username': { $regex: text, $options: "i" }}, function(err, users) { - if (err) { - return next(err); - } - var content = { - users: users, - id: req.user._id - } - res.send(content) - }) -}) +// // searches for users by username, and sends the list +// router.post('/search', async function(req, res, next) { +// var text = req.body.text +// User.find({'username': { $regex: text, $options: "i" }}, function(err, users) { +// if (err) { +// return res.status(400).send(err) +// } +// var content = { +// users: users, +// id: req.user._id +// } +// return res.status(200).send(content); +// }) +// }) // searches for users by username, and sends the list router.post('/search-page/user', async function(req, res, next) { var text = req.body.text User.find({'username': { $regex: text, $options: "i" }}, function(err, users) { - if (err) return next(err); + if (err) return res.status(400).send(err) var content = { users: users, id: req.user._id } - res.send(content) + return res.status(200).send(content); }) }) @@ -65,12 +65,12 @@ router.post('/search-page/user', async function(req, res, next) { router.post('/search-page/post', async function(req, res, next) { var text = req.body.text Post.find({'content': { $regex: text, $options: "i" }}, function(err, posts) { - if (err) return next(err); + if (err) return res.status(400).send(err) var content = { posts: posts, id: req.user._id } - res.send(content) + return res.status(200).send(content); }) }) @@ -78,7 +78,7 @@ router.post('/search-page/post', async function(req, res, next) { router.post('/search-page/song', async function(req, res, next) { var text = req.body.text Post.find({'songTitle': { $regex: text, $options: "i" }}, function(err, posts) { - if (err) return next(err); + if (err) return res.status(400).send(err) var content = { posts: posts, id: req.user._id @@ -87,20 +87,20 @@ router.post('/search-page/song', async function(req, res, next) { }) }) -// renders the search page -router.get("/search/:search", function(req, res, next) { - var search = req.params.search - return res.render("search", {search: search}) -}) +// // renders the search page +// router.get("/search/:search", function(req, res, next) { +// var search = req.params.search +// return res.render("search", {search: search}) +// }) -// renders the search page -router.get("/search", function(req, res, next) { - res.render("search", {search: false}) -}) +// // renders the search page +// router.get("/search", function(req, res, next) { +// res.render("search", {search: false}) +// }) -router.get("/search/user", function(req, res, next) { +// router.get("/search/user", function(req, res, next) { -}) +// }) module.exports = router \ No newline at end of file diff --git a/frontend-react/package-lock.json b/frontend-react/package-lock.json index 43b3b87..8307e0d 100644 --- a/frontend-react/package-lock.json +++ b/frontend-react/package-lock.json @@ -2984,6 +2984,14 @@ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.1.2.tgz", "integrity": "sha512-V+Nq70NxKhYt89ArVcaNL9FDryB3vQOd+BFXZIfO3RP6rwtj+2yqqqdHEkacutglPaZLkJeuXKCjCJDMGPtPqg==" }, + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -3585,6 +3593,11 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, + "bootstrap": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.0.tgz", + "integrity": "sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -7011,6 +7024,19 @@ "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" }, + "history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "requires": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -7021,6 +7047,14 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, "hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -9856,6 +9890,15 @@ "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" }, + "mini-create-react-context": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", + "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", + "requires": { + "@babel/runtime": "^7.12.1", + "tiny-warning": "^1.0.3" + } + }, "mini-css-extract-plugin": { "version": "0.11.3", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.3.tgz", @@ -12307,6 +12350,57 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", "integrity": "sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==" }, + "react-router": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", + "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", + "requires": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "mini-create-react-context": "^0.4.0", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "requires": { + "isarray": "0.0.1" + } + } + } + }, + "react-router-dom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", + "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", + "requires": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.2.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + } + }, + "react-script-tag": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/react-script-tag/-/react-script-tag-1.1.2.tgz", + "integrity": "sha512-4oR9ntvGfX4igiP77vK310ru5Oa77xO7CeYB3Xi9lu0qsikpGpK1Kq1WMFocvy8U4fQFq8ovtrZS/8adfCziFw==" + }, "react-scripts": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-4.0.2.tgz", @@ -12743,6 +12837,11 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" }, + "resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -14399,6 +14498,16 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, + "tiny-invariant": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", + "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" + }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -14874,6 +14983,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/frontend-react/package.json b/frontend-react/package.json index 81d26c4..4c2cb83 100644 --- a/frontend-react/package.json +++ b/frontend-react/package.json @@ -6,8 +6,12 @@ "@testing-library/jest-dom": "^5.11.9", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.7.1", + "axios": "^0.21.1", + "bootstrap": "^4.6.0", "react": "^17.0.1", "react-dom": "^17.0.1", + "react-router-dom": "^5.2.0", + "react-script-tag": "^1.1.2", "react-scripts": "4.0.2", "web-vitals": "^1.1.0" }, diff --git a/frontend-react/public/js/homepage.js b/frontend-react/public/js/homepage.js new file mode 100644 index 0000000..908324a --- /dev/null +++ b/frontend-react/public/js/homepage.js @@ -0,0 +1,15 @@ +function styleHomePage() { + if (window.innerWidth < 992) { + document.querySelector("#home-screen-content").classList.remove("home-screen-content") + } + else { + document.querySelector("#home-screen-content").classList.add("home-screen-content") + } + } + +window.addEventListener("resize", () => { + styleHomePage() +}) +window.addEventListener("load", () => { + styleHomePage() +}) \ No newline at end of file diff --git a/frontend-react/public/js/script.js b/frontend-react/public/js/script.js new file mode 100644 index 0000000..3208a30 --- /dev/null +++ b/frontend-react/public/js/script.js @@ -0,0 +1,79 @@ +var dates = document.querySelectorAll(".date") + dates.forEach(date => { + var d = date.textContent + date.textContent = timeSince(Date.now() - d) + setInterval(() => { + date.textContent = timeSince(Date.now() - d) + }, 5000); + }) +function timeSince(date) { + var time = Math.floor(date / 1000) + + if (time < 60) return time + "s" + + time = Math.floor(time / 60) + + if (time < 60) return time + "min" + + time = Math.floor(time / 60) + + if (time < 24) return time + "hr" + + time = Math.floor(time / 24) + + if (time < 7) return time + "d" + time = Math.floor(time / 7) + + if (time < 30) return time + "w" + + time = Math.floor(time / 30) + + if (time < 52) return time + "m" + + return time + "yr" +} + +window.addEventListener("resize", function() { + document.querySelectorAll('iframe').forEach(song => { + song.style.width = song.parentElement.width + }) +}) + +document.querySelectorAll(".post-dropdown-arrow ").forEach(arrow => { + arrow.addEventListener("click", function() { + if (arrow.classList.contains("fa-angle-down")) { + arrow.classList.remove("fa-angle-down") + arrow.classList.add("fa-angle-up") + var parent = arrow.parentElement + var dropdown = parent.querySelector("ul") + dropdown.classList.remove("display-none") + } + else { + arrow.classList.add("fa-angle-down") + arrow.classList.remove("fa-angle-up") + var parent = arrow.parentElement + var dropdown = parent.querySelector("ul") + dropdown.classList.add("display-none") + } + }) +}) + +var dropdowns = document.querySelectorAll(".post-dropdown") + +dropdowns.forEach(dd => { + var arrow = dd.childNodes[1] + var dropdown = dd.childNodes[3] + window.addEventListener("click", function(event) { + + var isClickInside = arrow.contains(event.target) || dropdown.contains(event.target) + if (!isClickInside) { + if (arrow.classList.contains("fa-angle-up")) { + arrow.classList.toggle("fa-angle-down") + arrow.classList.toggle("fa-angle-up") + } + dropdown.classList.add("d-none") + } else { + dropdown.classList.remove("d-none") + } + }) +}) diff --git a/frontend-react/public/js/search.js b/frontend-react/public/js/search.js new file mode 100644 index 0000000..d75e49b --- /dev/null +++ b/frontend-react/public/js/search.js @@ -0,0 +1,94 @@ + + +function setSearchBar() { + var newWidth = document.querySelector(".search-span") + document.querySelector(".search-display-items").style.width = newWidth + if (window.innerWidth < 768) { + document.querySelector(".search-span").style.display = "none" + document.querySelector("#search-link").href = "/search" + } + else { + document.querySelector(".search-span").style.display = "inline-block" + document.querySelector("#search-link").removeAttribute('href') + } + } + + +window.addEventListener("resize", function() { + setSearchBar() +}) + +window.addEventListener("load", () => { + setSearchBar() +}) + +window.addEventListener("click", function(event) { + var searchBar = document.querySelector("#search-input") + var searchResults = document.querySelector(".search-display-items") + + var isClickInside = searchBar.contains(event.target) || searchResults.contains(event.target) + if (!isClickInside) { + document.querySelector(".search-display-items").classList.add("d-none") + } +}) + +document.querySelector("#search-input").addEventListener("input", function() { + if (this.value === "") { + for (var i = 1; i < 10; i += 2) { + document.querySelector(".search-display-items").childNodes[i].childNodes[0].textContent = "" + } + document.querySelector(".search-display-items").classList.add("d-none") + } + else { + document.querySelector(".search-display-items").classList.remove("d-none") + var items = { + text: this.value, + } + fetch("/search", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(items), + }) + .then(async function(r) { + var res = await r.json(); + return res + }).then(res => { + var result = res.users + var num = result.length + var listItems = document.querySelector(".search-display-items") + for (var i = 1; i < 10; i += 2) { + document.querySelector(".search-display-items").childNodes[i].childNodes[0].textContent = "" + } + for (var i = 1; i < num * 2; i += 2) { + var item = listItems.childNodes[i] + item.classList.remove("d-none") + item.childNodes[0].textContent = result[i - Math.ceil(i / 2)].username + if (res.id === result[i - Math.ceil(i / 2)]._id) { + item.href = "/profile" + } + else { + item.href = "/profile/" + result[i - Math.ceil(i / 2)]._id + } + } + for (var i = 1; i < 10; i += 2) { + if (listItems.childNodes[i].childNodes[0].textContent === "") { + listItems.childNodes[i].classList.add("d-none") + } + } + document.querySelector("#sixth-text").textContent = this.value + var searchValue = document.querySelector("#search-sixth-link") + searchValue.href = "/search/" + this.value + }).catch(error => { + console.log(error) + }) + } +}) + +// document.querySelector("#search-input").addEventListener("click", function() { +// if (this.value === "") { +// console.log(document.querySelector(".search-display-items")) +// } +// }) + diff --git a/frontend-react/public/js/spotify.js b/frontend-react/public/js/spotify.js new file mode 100644 index 0000000..078a7c9 --- /dev/null +++ b/frontend-react/public/js/spotify.js @@ -0,0 +1,147 @@ +const options = { + headers: { + 'Content-Type':'application/x-www-form-urlencoded', + 'Authorization': 'Basic ZjEyMDdhYjNhODcwNDk5MmIyMjZiMTI1ODdhYTdjMDI6NGY3YWMxMWI1NTk5NDFiNmExZmMxMTI4MWQ4NDljZTA',} +}; + +var postSong = document.querySelector("input.song") +var postList = document.querySelector(".song-display-items") +var postFirst = document.querySelector(".first") +var postSecond = document.querySelector(".second") +var postThird = document.querySelector(".third") +var postFourth = document.querySelector(".fourth") +var postFifth = document.querySelector(".fifth") +var postListSongs = [postFirst, postSecond, postThird, postFourth, postFifth] +var postSongLink = document.querySelector("input[name='songlink']") + +var editSong = document.querySelector("input.edit-song") +var editList = document.querySelector(".song-edit-display-items") +var editFirst = document.querySelector(".edit-first") +var editSecond = document.querySelector(".edit-second") +var editThird = document.querySelector(".edit-third") +var editFourth = document.querySelector(".edit-fourth") +var editFifth = document.querySelector(".edit-fifth") +var editListSongs = [editFirst, editSecond, editThird, editFourth, editFifth] +var editSongLink = document.querySelector("input[name='editsonglink']") + +window.addEventListener("click", function(event) { + var isClickInside = postSong.contains(event.target) || editSong.contains(event.target) || postList.contains(event.target) + if (!isClickInside) { + postList.classList.add("display-none") + editList.classList.add("display-none") + } + // } else { + // if (editSong.value !== '') { + // editList.classList.remove("display-none") + // } + // if (postSong.value !== '') { + // postList.classList.remove("display-none") + // } + // } +}) + +var spotifyFunction = function (song, songLink, first, second, third, fourth, fifth, list) { + axios.post('https://accounts.spotify.com/api/token', 'grant_type=client_credentials', options).then((response) => { + var at = response.data.access_token + var op = { + headers: { + 'Content-Type':'application/x-www-form-urlencoded', + 'Authorization': 'Bearer ' + at,} + }; + + + var qs = createQueryString(song.value) + if (qs === "") { + if (song == editSong) { + } + return list.classList.add("display-none") + } + axios.get("https://api.spotify.com/v1/search" + qs, op).then((response) => { + list.classList.remove("display-none") + first.textContent = response.data.tracks.items[0].name + ", " + response.data.tracks.items[0].artists[0].name + first.addEventListener("click", function() { + songLink.value = "https://open.spotify.com/embed/track/" + response.data.tracks.items[0].id + }) + + second.textContent = response.data.tracks.items[1].name + ", " + response.data.tracks.items[1].artists[0].name + second.addEventListener("click", function() { + songLink.value = "https://open.spotify.com/embed/track/" + response.data.tracks.items[1].id + }) + + third.textContent = response.data.tracks.items[2].name + ", " + response.data.tracks.items[2].artists[0].name + third.addEventListener("click", function() { + songLink.value = "https://open.spotify.com/embed/track/" + response.data.tracks.items[2].id + }) + + fourth.textContent = response.data.tracks.items[3].name + ", " + response.data.tracks.items[3].artists[0].name + fourth.addEventListener("click", function() { + songLink.value = "https://open.spotify.com/embed/track/" + response.data.tracks.items[3].id + }) + fifth.textContent = response.data.tracks.items[4].name + ", " + response.data.tracks.items[4].artists[0].name + fifth.addEventListener("click", function() { + songLink.value = "https://open.spotify.com/embed/track/" + response.data.tracks.items[4].id + }) + }).catch(function (error) { + console.log(error) + }) + + }).catch(function (error) { + console.log(error) + }) +} + +postSong.addEventListener("input", () => { + spotifyFunction(postSong, postSongLink, postFirst, postSecond, postThird, postFourth, postFifth, postList) +}) + +editSong.addEventListener("input", () => { + spotifyFunction(editSong, editSongLink, editFirst, editSecond, editThird, editFourth, editFifth, editList) +}) + +editSong.addEventListener("click", () => { + if (editSong.value === '') { + editList.classList.add("display-none") + } +}) + +function changeValue(element, song, list) { + song.value = element.textContent + list.classList.add("display-none") + if (song == editSong) { + }2 +} + +postListSongs.forEach(song => { + song.addEventListener("click", function() { + changeValue(this, postSong, postList) + }) +}) + +editListSongs.forEach(song => { + song.addEventListener("click", function() { + changeValue(this, editSong, editList) + }) +}) + +function createQueryString(song) { + var songArr = song.split("") + var str = "" + if (song.length == 0) { + return "" + } + songArr.forEach(letter => { + if (letter === " ") { + str += "+" + } + else if (letter === '"') { + str += "%22" + } + else if (letter === "'") { + str += "%27" + } + else { + str += letter + } + }); + return "?q=" + str + "&type=track" +} \ No newline at end of file diff --git a/frontend-react/src/App.js b/frontend-react/src/App.js index 3784575..e2b022e 100644 --- a/frontend-react/src/App.js +++ b/frontend-react/src/App.js @@ -1,23 +1,15 @@ import logo from './logo.svg'; import './App.css'; +import Routes from './components/router' +import Navbar from './components/navbar' function App() { return (
-
- logo -

- Edit src/App.js and save to reload. -

- - Learn React - -
+ +
+ +
); } diff --git a/frontend-react/src/components/home.js b/frontend-react/src/components/home.js new file mode 100644 index 0000000..b398518 --- /dev/null +++ b/frontend-react/src/components/home.js @@ -0,0 +1,51 @@ +import { React, Component } from "react"; + +import {appendScript} from '../js/utils'; +import PostForm from "./postform"; + +import Sidetab from './sidetab'; + +import axios from 'axios'; + +export default class Home extends Component { + + state = { + posts: [], + popular: [] + } + + componentDidMount() { + appendScript('/js/homepage.js'); + appendScript('https://unpkg.com/axios/dist/axios.min.js'); + appendScript('/js/spotify.js'); + appendScript('/js/script.js'); + this.getHomeInfo(); + } + + getHomeInfo() { + const url = 'http://localhost:3000/home'; + console.log(url); + axios.get(url) + .then(res => { + console.log(res); + }) + } + + render() { + return ( +
+
+
+ + {/* <%- include("partials/side-tab", {popular: popular}) %> */} +
+
+ + {/* <%- include("partials/post-form") %> */} + {/* <%- include("partials/post-list", {post: posts, user: user}) %> */} +
+
+
+ ); + } +} diff --git a/frontend-react/src/components/navbar.js b/frontend-react/src/components/navbar.js new file mode 100644 index 0000000..44567a2 --- /dev/null +++ b/frontend-react/src/components/navbar.js @@ -0,0 +1,50 @@ +import { React, Component } from "react"; + +import {appendScript} from '../js/utils'; + +export default class Navbar extends Component { + + componentDidMount() { + appendScript('/js/search.js') + } + + render() { + return ( + + ); + } +} diff --git a/frontend-react/src/components/postform.js b/frontend-react/src/components/postform.js new file mode 100644 index 0000000..43fc83a --- /dev/null +++ b/frontend-react/src/components/postform.js @@ -0,0 +1,35 @@ +import { React, Component } from "react"; + +import {appendScript} from '../js/utils'; + +export default class PostForm extends Component { + + componentDidMount() { + } + + render() { + return ( +
+
+ +
+
+ + + +
+ + +
+ +
+
+ ); + } +} diff --git a/frontend-react/src/components/router.js b/frontend-react/src/components/router.js new file mode 100644 index 0000000..ed36266 --- /dev/null +++ b/frontend-react/src/components/router.js @@ -0,0 +1,22 @@ +import React from "react"; +import { + BrowserRouter as Router, + Switch, + Route, + Link +} from "react-router-dom"; +import Home from './home' + +export default class Routes extends React.Component { + render() { + return ( + + + + + + + + ); + } +} diff --git a/frontend-react/src/components/sidetab.js b/frontend-react/src/components/sidetab.js new file mode 100644 index 0000000..4521b7e --- /dev/null +++ b/frontend-react/src/components/sidetab.js @@ -0,0 +1,22 @@ +import { React, Component } from "react"; + +import {appendScript} from '../js/utils'; + +export default class Sidetab extends Component { + + componentDidMount() { + } + + render() { + return ( +
+

What your friends are listening to:

+ {/* */} +
+ ); + } +} diff --git a/frontend-react/src/index.css b/frontend-react/src/index.css index ec2585e..c3286b0 100644 --- a/frontend-react/src/index.css +++ b/frontend-react/src/index.css @@ -1,13 +1,310 @@ + +@import url('https://fonts.googleapis.com/css2?family=Satisfy&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Lato&display=swap'); + +html { + height: 100%; + width: 100%; + color: #30475e; + font-family: 'Lato', sans-serif; + overflow: hidden; +} body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + height: 100%; + width: 100%; + color: #30475e; + font-family: 'Lato', sans-serif; + overflow: auto; +} + +.content { + margin-top: 100px !important; +} + +.panel { + margin-top: 10px; +} + +.edit-window { + position: fixed; + width: 35%; + height: auto !important; + margin: 10% 35%; + padding: 25px; + background-color: #fcf8e8; + filter: blur(0px) !important; + z-index: 999999999; +} +#edit-window-exit { + cursor: pointer; +} + +.logo { + font-family: 'Satisfy', cursive; + font-size: 2em !important; + color: #f1935c !important; +} + +.profile-pic-tile { + width: 50px; + height: 50px; + object-fit: cover; + margin-right: 10px; + border-radius: 50%; +} + +.post-text-content { + font-size: 1.14rem; +} + +/* #home-screen-content { + border: 5px solid red; +} */ + +.home-screen-content { + position: fixed; + width: 25%; + margin-top: 50px; +} +.home-screen-content h3 { + width: 80%; +} +#home-screen-content ul { + width: 100%; + list-style: none; + padding-left: 0 !important; +} + +.profile { + position: fixed !important; +} +.btn-primary { + background-color: #f1935c; + border: none; +} + +.btn-primary:hover { + background-color: #dc6622; + transition: .5s; +} + +.posts { + list-style: none; + padding: 0 0 25px 0; +} + +.song-display-items, .song-edit-display-items { + list-style: none; + background: white; + border: 1px solid black; + border-top: none !important; + padding-inline-start: 0 !important; +} + +.song-display-items li, .song-edit-display-items li { + padding: 5px 0 5px 25px; + border-top: 1px #dddddd solid; + cursor: pointer; + border-bottom: 1px #dddddd solid; +} +.song-display-items li:hover, .song-edit-display-items li:hover { + background: #dddddd +} + +.post { + background-color: #fcf8e8; + border-radius: 25px; + padding: 15px 15px 10px 15px; + margin-bottom: 15px; + cursor: pointer; +} + +.log-in-update-label { + font-size: .75em; +} +.displayed-post { + cursor: pointer; +} + +.spotify-embeds { + width: 100%; + } + +.spotify-embed { + left: 0; + width: 100%; + height: 0; + position: relative; + padding-top: 80px; +} +.spotify-embed .song-tile { + border: 0; + top: 0; + left: 0; + width: 100%; + height: 100%; + position: absolute; +} + +.profile-pic-container { + width: 240px; + height: 240px; + margin: 0 auto; + position: relative; + overflow: hidden; + border-radius: 50%; +} +.profile-pic { + margin: 0 auto; + height: inherit; + width: inherit; + object-fit: cover; +} + +.post-dropdown-arrow { + margin-top: 10px; + margin-right: 10px; + float: right; +} + +.post-dropdown-arrow-list { + z-index: 999999999; + position: relative; + margin-top: 10px; + margin-right: 10px; + float: right; + list-style: none; + background: white; + margin-top: 25px; + margin-right: -15px; + padding-left: 0; +} +.post-dropdown-arrow-list li { + padding: 3px 40px 3px 8px; +} +.post-dropdown-arrow-list li a { + color: #30475e; +} +.post-dropdown-arrow-list li:hover { + background: #f1f1f1 +} + +.display-none { + display: none; +} + +.post-dropdown-arrow-list a { + color: #f1935c; + text-decoration: none; +} +.post a.username { + color: #f1935c; + text-decoration: none; +} +.post a.name { + color: #30475e; + text-decoration: none; +} +.post a.name:hover { + text-decoration: underline; +} +.post-content { + font-weight: 400; +} + +.navbar-light { + background-color: #fcf8e8 !important; +} + +.navbar-nav li { + margin: 0 15px; + display: inline-block !important; +} + +.fa-search { + cursor: pointer; +} + +.underline { + height: 3px; + background-color: transparent; + width: 0%; + transition: width 0.2s, background-color 0.5s; + margin: 0 auto; +} + +li.nav-item, li.nav-item .fas { + color: #30475e; +} + +li.nav-item:hover .underline { + background-color: #30475e; + width: 100%; +} + +ul.navbar-nav{ + display: inline !important; +} + +.search-span { + display: none; } -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; +@media (max-width: 1532px) { + .edit-window { + width: 60%; + margin: 10% 20%; + } } + +@media (max-width: 992px) { + .profile-pic-container { + width: 150px; + height: 150px; + margin: 0; + } + .profile-pic-container h1 { + float: left; + } + .profile-pic { + margin: 0; + } + +} + +@media (max-width: 768px) { + .navbar-right { + margin-left: 0 !important; + } + .navbar-nav li { + display: inline-block; + } + .search-span { + overflow: hidden; + vertical-align: middle; + } + + #search-input { + transform: translateX(100%); + opacity: 0; + width: 50%; + position: relative; + z-index: 99999; + transition: opacity 1s, transform 1s; + } + #search-input.show { + transform: translateX(0); + display: inline-block; + opacity: 1; + } + .profile-pic-container { + width: 80px; + height: 80px; + } + + .edit-window { + width: 90%; + margin: 10% 5%; + } + +} \ No newline at end of file diff --git a/frontend-react/src/index.js b/frontend-react/src/index.js index 9fd2e10..e5f893d 100644 --- a/frontend-react/src/index.js +++ b/frontend-react/src/index.js @@ -1,8 +1,10 @@ import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; +// import './search.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; +import 'bootstrap/dist/css/bootstrap.css'; ReactDOM.render( diff --git a/frontend-react/src/js/utils.js b/frontend-react/src/js/utils.js new file mode 100644 index 0000000..aa55f92 --- /dev/null +++ b/frontend-react/src/js/utils.js @@ -0,0 +1,7 @@ +export const appendScript = (scriptToAppend) => { + const script = document.createElement("script"); + script.src = scriptToAppend; + script.async = true; + console.log(script); + document.body.appendChild(script); +} \ No newline at end of file diff --git a/frontend-react/src/search.css b/frontend-react/src/search.css new file mode 100644 index 0000000..e69de29 diff --git a/frontend/public/css/style.css b/frontend/public/css/style.css index e7ce453..8068696 100644 --- a/frontend/public/css/style.css +++ b/frontend/public/css/style.css @@ -40,7 +40,7 @@ body { .logo { font-family: 'Satisfy', cursive; - font-size: 2em; + font-size: 2.0rem !important; color: #f1935c !important; } @@ -249,6 +249,159 @@ ul.navbar-nav{ display: none; } + +.search-span { + width: 210px; +} + +.search-display-items { + position: fixed; + width: 195px !important; + list-style: none; + z-index: 99999; + background: white; + border: 1px solid black; + border-top: none !important; + padding-inline-start: 0 !important; +} +.search-display-items li { + display: block !important; + padding: 5px 0 5px 25px; + margin: 0; + z-index: 999999; + border-top: 1px #dddddd solid; + cursor: pointer; + border-bottom: 1px #dddddd solid; +} + +.search-display-items li:hover { + background: #dddddd +} + +.search-display-items a { + text-decoration: none; + color: #30475e; +} + + +.search-page-span { + width: 80%; + margin: 0 auto !important; + display: block !important; +} +#search-page-input { + width: 50%; + margin: 0 auto !important; +} + +.search-page-display-items { + list-style: none; + z-index: 99999; + width: 50%; + margin: 0 auto; + padding: 0; +} +.search-page-display-items li { + display: block !important; + padding: 5px 0 5px 25px; + margin: 0 auto !important; + z-index: 999999; + height: 60px; + border-top: 1px #dddddd solid; + cursor: pointer; + border-bottom: 1px #dddddd solid; +} + +.search-page-display-items li:hover { + background: #dddddd +} + +.search-page-display-items a { + text-decoration: none; + color: #30475e; +} + +.search-page-image { + height: 45px; + width: 45px; + border-radius: 50%; + object-fit: cover; + margin: auto 0 !important; + float: left; +} + +.search-page-username { + margin-left: 10px; + float: left; +} +.search-page-username p { + margin: 0; +} +.search-page-username p.username { + font-size: 1.1em; + font-weight: 700; +} + +.search-parameters { + list-style-type: none; + margin: 10px 0; + padding: 0; +} + +.search-parameters li { + display: inline-block !important; + margin: 0 0.83%; + width: 18%; + height: 40px; + position: relative; +} + +.search-parameters label, +.search-parameters input { + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} +.search-parameters input[type="radio"] { + opacity: 0.01; + z-index: 100; +} + +.search-parameters input[type="radio"]:checked+label, +.Checked+label { + background: #f1935c; + color: white; + border: none; +} + +.search-parameters label { + text-align: center; + padding: 3px; + border-radius: 3px; + border: 1px solid #CCC; + cursor: pointer; + z-index: 90; +} + +.search-parameters label:hover { + background: #DDD; +} + +@media (max-width: 792px) { + .search-page-span { + width: 100%; + } + #search-page-input { + width: 80%; + } + .search-page-display-items { + width: 80%; + } +} + @media (max-width: 1532px) { .edit-window { width: 60%; diff --git a/frontend/routes/home.js b/frontend/routes/home.js index e408030..5536535 100644 --- a/frontend/routes/home.js +++ b/frontend/routes/home.js @@ -8,11 +8,11 @@ var middleware = require("../middleware/index") // renders the home page with posts in chronological order router.get("/home", middleware.isLoggedIn, function(req, res) { User.findById(req.user._id, function(err, user) { - if (err) return res.status(400).send(err) + if (err) return next(err); var following = user.following.map(item => item.username) following.push(user.username) Post.find({"author.username": {$in : following}}, function(err, posts) { - if (err) return res.status(400).send(err) + if (err) console.log(err); var songs = 0; var popular = posts.map(post => post.songTitle) var popularFive = [] @@ -28,36 +28,36 @@ router.get("/home", middleware.isLoggedIn, function(req, res) { } songs ++ } - return res.status(200).send({posts: posts, popular: popularFive}) + res.render("home", {posts: posts, popular: popularFive}) }) }) }) -// // searches for users by username, and sends the list -// router.post('/search', async function(req, res, next) { -// var text = req.body.text -// User.find({'username': { $regex: text, $options: "i" }}, function(err, users) { -// if (err) { -// return res.status(400).send(err) -// } -// var content = { -// users: users, -// id: req.user._id -// } -// return res.status(200).send(content); -// }) -// }) +// searches for users by username, and sends the list +router.post('/search', async function(req, res, next) { + var text = req.body.text + User.find({'username': { $regex: text, $options: "i" }}, function(err, users) { + if (err) { + return next(err); + } + var content = { + users: users, + id: req.user._id + } + res.send(content) + }) +}) // searches for users by username, and sends the list router.post('/search-page/user', async function(req, res, next) { var text = req.body.text User.find({'username': { $regex: text, $options: "i" }}, function(err, users) { - if (err) return res.status(400).send(err) + if (err) return next(err); var content = { users: users, id: req.user._id } - return res.status(200).send(content); + res.send(content) }) }) @@ -65,12 +65,12 @@ router.post('/search-page/user', async function(req, res, next) { router.post('/search-page/post', async function(req, res, next) { var text = req.body.text Post.find({'content': { $regex: text, $options: "i" }}, function(err, posts) { - if (err) return res.status(400).send(err) + if (err) return next(err); var content = { posts: posts, id: req.user._id } - return res.status(200).send(content); + res.send(content) }) }) @@ -78,7 +78,7 @@ router.post('/search-page/post', async function(req, res, next) { router.post('/search-page/song', async function(req, res, next) { var text = req.body.text Post.find({'songTitle': { $regex: text, $options: "i" }}, function(err, posts) { - if (err) return res.status(400).send(err) + if (err) return next(err); var content = { posts: posts, id: req.user._id @@ -87,20 +87,20 @@ router.post('/search-page/song', async function(req, res, next) { }) }) -// // renders the search page -// router.get("/search/:search", function(req, res, next) { -// var search = req.params.search -// return res.render("search", {search: search}) -// }) +// renders the search page +router.get("/search/:search", function(req, res, next) { + var search = req.params.search + return res.render("search", {search: search}) +}) -// // renders the search page -// router.get("/search", function(req, res, next) { -// res.render("search", {search: false}) -// }) +// renders the search page +router.get("/search", function(req, res, next) { + res.render("search", {search: false}) +}) -// router.get("/search/user", function(req, res, next) { +router.get("/search/user", function(req, res, next) { -// }) +}) module.exports = router \ No newline at end of file diff --git a/frontend/uploads/41de2697403357b3c658b92979783295 b/frontend/uploads/41de2697403357b3c658b92979783295 new file mode 100644 index 0000000..d338bd3 Binary files /dev/null and b/frontend/uploads/41de2697403357b3c658b92979783295 differ