diff --git a/code/package-lock.json b/code/package-lock.json index f7e97e1e9..8e5e82254 100644 --- a/code/package-lock.json +++ b/code/package-lock.json @@ -8,13 +8,17 @@ "name": "technigo-react-starter", "version": "1.0.0", "dependencies": { + "@lottiefiles/react-lottie-player": "^3.5.3", "babel-eslint": "^10.1.0", + "cors": "^2.8.5", + "date-fns": "^2.30.0", "eslint": "^8.21.0", "eslint-config-airbnb": "^19.0.4", "eslint-plugin-import": "^2.26.0", "eslint-plugin-jsx-a11y": "^6.6.1", "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", + "lottie-react": "^2.4.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -1849,11 +1853,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", + "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.11" }, "engines": { "node": ">=6.9.0" @@ -3062,6 +3066,17 @@ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", "dev": true }, + "node_modules/@lottiefiles/react-lottie-player": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/@lottiefiles/react-lottie-player/-/react-lottie-player-3.5.3.tgz", + "integrity": "sha512-6pGbiTMjGnPddR1ur8M/TIDCiogZMc1aKIUbMEKXKAuNeYwZ2hvqwBJ+w5KRm88ccdcU88C2cGyLVsboFlSdVQ==", + "dependencies": { + "lottie-web": "^5.10.2" + }, + "peerDependencies": { + "react": "16 - 18" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -5726,6 +5741,18 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cosmiconfig": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", @@ -6205,6 +6232,21 @@ "node": ">=10" } }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -11879,6 +11921,23 @@ "loose-envify": "cli.js" } }, + "node_modules/lottie-react": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/lottie-react/-/lottie-react-2.4.0.tgz", + "integrity": "sha512-pDJGj+AQlnlyHvOHFK7vLdsDcvbuqvwPZdMlJ360wrzGFurXeKPr8SiRCjLf3LrNYKANQtSsh5dz9UYQHuqx4w==", + "dependencies": { + "lottie-web": "^5.10.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/lottie-web": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/lottie-web/-/lottie-web-5.11.0.tgz", + "integrity": "sha512-9vSt0AtdOH98GKDXwD5LPfFg9Pcmxt5+1BllAbudKM5iqPxpJnJUfuGaP45OyudDrESCOBgsjnntVUTygBNlzw==" + }, "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -14717,9 +14776,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "node_modules/regenerator-transform": { "version": "0.15.0", @@ -16571,7 +16630,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -18790,11 +18848,11 @@ } }, "@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", + "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.11" } }, "@babel/runtime-corejs3": { @@ -19651,6 +19709,14 @@ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", "dev": true }, + "@lottiefiles/react-lottie-player": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/@lottiefiles/react-lottie-player/-/react-lottie-player-3.5.3.tgz", + "integrity": "sha512-6pGbiTMjGnPddR1ur8M/TIDCiogZMc1aKIUbMEKXKAuNeYwZ2hvqwBJ+w5KRm88ccdcU88C2cGyLVsboFlSdVQ==", + "requires": { + "lottie-web": "^5.10.2" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -21665,6 +21731,15 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "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" + } + }, "cosmiconfig": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", @@ -21996,6 +22071,14 @@ "whatwg-url": "^8.0.0" } }, + "date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "requires": { + "@babel/runtime": "^7.21.0" + } + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -26190,6 +26273,19 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lottie-react": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/lottie-react/-/lottie-react-2.4.0.tgz", + "integrity": "sha512-pDJGj+AQlnlyHvOHFK7vLdsDcvbuqvwPZdMlJ360wrzGFurXeKPr8SiRCjLf3LrNYKANQtSsh5dz9UYQHuqx4w==", + "requires": { + "lottie-web": "^5.10.2" + } + }, + "lottie-web": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/lottie-web/-/lottie-web-5.11.0.tgz", + "integrity": "sha512-9vSt0AtdOH98GKDXwD5LPfFg9Pcmxt5+1BllAbudKM5iqPxpJnJUfuGaP45OyudDrESCOBgsjnntVUTygBNlzw==" + }, "lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -28126,9 +28222,9 @@ } }, "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "regenerator-transform": { "version": "0.15.0", @@ -29522,8 +29618,7 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, "w3c-hr-time": { "version": "1.0.2", diff --git a/code/package.json b/code/package.json index 68869f589..6657355f6 100644 --- a/code/package.json +++ b/code/package.json @@ -3,13 +3,17 @@ "version": "1.0.0", "private": true, "dependencies": { + "@lottiefiles/react-lottie-player": "^3.5.3", "babel-eslint": "^10.1.0", + "cors": "^2.8.5", + "date-fns": "^2.30.0", "eslint": "^8.21.0", "eslint-config-airbnb": "^19.0.4", "eslint-plugin-import": "^2.26.0", "eslint-plugin-jsx-a11y": "^6.6.1", "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", + "lottie-react": "^2.4.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/code/public/images/michael-dziedzic-0W4XLGITrHg-unsplash.jpeg b/code/public/images/michael-dziedzic-0W4XLGITrHg-unsplash.jpeg new file mode 100644 index 000000000..4bcdc2c81 Binary files /dev/null and b/code/public/images/michael-dziedzic-0W4XLGITrHg-unsplash.jpeg differ diff --git a/code/public/index.html b/code/public/index.html index e6730aa66..0d6e02dbd 100644 --- a/code/public/index.html +++ b/code/public/index.html @@ -13,7 +13,8 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - Technigo React App + + Happy Thoughts Feed diff --git a/code/src/App.js b/code/src/App.js index f2007d229..9fc1b9dad 100644 --- a/code/src/App.js +++ b/code/src/App.js @@ -1,9 +1,8 @@ import React from 'react'; +import { ThoughtFeed } from './components/ThoughtFeed'; export const App = () => { return ( -
- Find me in src/app.js! -
+ ); } diff --git a/code/src/SingleThought.js b/code/src/SingleThought.js new file mode 100644 index 000000000..e69de29bb diff --git a/code/src/components/Background.js b/code/src/components/Background.js new file mode 100644 index 000000000..92daa91f0 --- /dev/null +++ b/code/src/components/Background.js @@ -0,0 +1,17 @@ +import React from 'react' + +export const Background = () => { + return ( + + + + + + + + + + + + ) +} diff --git a/code/src/components/Loader.js b/code/src/components/Loader.js new file mode 100644 index 000000000..d1b310dce --- /dev/null +++ b/code/src/components/Loader.js @@ -0,0 +1,13 @@ +import React from 'react'; +import { Player } from '@lottiefiles/react-lottie-player'; + +export const Loader = () => { + return ( + + ) +} \ No newline at end of file diff --git a/code/src/components/ThoughtFeed.js b/code/src/components/ThoughtFeed.js new file mode 100644 index 000000000..945c3a277 --- /dev/null +++ b/code/src/components/ThoughtFeed.js @@ -0,0 +1,100 @@ +/* eslint no-underscore-dangle: 0 */ +import React, { useState, useEffect } from 'react'; +import { formatDistance } from 'date-fns'; +import { ThoughtForm } from './ThoughtForm'; +import { Loader } from './Loader'; +import { Background } from './Background'; + +/* const API = 'https://project-happy-thoughts-api-3t72lksv4a-lz.a.run.app' + */ +export const ThoughtFeed = () => { + const [thoughtList, setThoughtList] = useState([]) + const [isLoading, setIsLoading] = useState(false) + const [likeCount, setLikeCount] = useState(0) + + const fetchThoughts = () => { + fetch('https://project-happy-thoughts-api-3t72lksv4a-lz.a.run.app/thoughts') + .then((response) => response.json()) + .then((data) => setThoughtList(data.response)) + .catch((error) => console.log(error)) + .finally(() => { + setTimeout(() => setIsLoading(false), 2000) + }) + } + + useEffect(() => { + setIsLoading(true); + fetchThoughts(); + }, []); + + const handleLikes = (_id) => { + setLikeCount(likeCount + 1) + const options = { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + } + fetch(`https://project-happy-thoughts-api-3t72lksv4a-lz.a.run.app/${_id}/like`, options) + .then((res) => { + return res.json(); + }) + .then((data) => { + const updatedThoughtList = thoughtList.map((thought) => { + if (thought._id === data.response._id) { + return { + ...thought, + heart: data.response.heart + }; + } + return thought; + }); + setThoughtList(updatedThoughtList); + }) + .catch((error) => console.error(error)); + } + + return ( +
+ +
+
+ +
+
+ {!isLoading && thoughtList.map((thought) => { + return ( +
+

{thought.message}

+
+
+ + x {thought.heart} +
+

+ {thought.category} + + {formatDistance(new Date(thought.createdAt), Date.now(), { addSuffix: true })} + + {`${thought.name === '' ? '' : 'by'}`} {thought.name} +

+
+
+ ) + })} +
+
+ {isLoading && ()} +
+ ) +} diff --git a/code/src/components/ThoughtForm.js b/code/src/components/ThoughtForm.js new file mode 100644 index 000000000..ce9d56aa2 --- /dev/null +++ b/code/src/components/ThoughtForm.js @@ -0,0 +1,105 @@ +import React, { useState, useEffect } from 'react'; + +export const ThoughtForm = ({ likeCount }) => { + const [newThought, setNewThought] = useState(''); + const [newName, setNewName] = useState(''); + const [newCategory, setNewCategory] = useState(); + const [minMaxCount, setMinMaxCount] = useState(false); + + const API = 'https://project-happy-thoughts-api-3t72lksv4a-lz.a.run.app/thoughts' + + const onFormSubmit = (event) => { + event.preventDefault() + if (newThought.length < 5 || newThought.length >= 141) { + return alert('Oh no, you have too few or too many characters! Please, try again.') + } else { + const options = { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ message: newThought, name: newName, category: newCategory }) + }; + fetch(`${API}`, options) + .then((response) => response.json()) + .then(() => { + setNewThought(''); + setNewName(''); + setNewCategory('') + window.location.reload(); + }) + } + } + + const onNewThoughtChange = (event) => { + setNewThought(event.target.value); + } + + const onNewNameChange = (event) => { + setNewName(event.target.value); + } + + const onNewCategoryChange = (event) => { + setNewCategory(event.target.value); + } + + useEffect(() => { + if (newThought.length < 5 || newThought.length >= 141) { + setMinMaxCount(true) + } else { + setMinMaxCount(false) + } + }, [newThought.length]); + + return ( +
+

What's making you happy right now?

+