diff --git a/README.md b/README.md index 6fc6d095..e995ca86 100644 --- a/README.md +++ b/README.md @@ -6,30 +6,18 @@ # Happy thoughts Project -In this week's project, you'll be able to practice your React state skills by fetching and posting data to an API. +This project allows users to post happy thoughts, view the most recent posts, and like posts by clicking a button. It uses the useEffect hook to handle side effects such as fetching recent posts from an API and updating the time since each thought was posted in real time. -## Getting Started with the Project - -### Dependency Installation & Startup Development Server - -Once cloned, navigate to the project's root directory and this project uses npm (Node Package Manager) to manage its dependencies. +### The Problem -The command below is a combination of installing dependencies, opening up the project on VS Code and it will run a development server on your terminal. +I tried to keep it simple and keep the focus on managing state and lifecycle events, rather than getting distracted with design issues like which font to use. In some ways having a design to copy helps to streamline the design process, but having to work within certain limitations can also feel like a challenge. (I actually started to look into tools that could help me identify fonts, but then backtracked and decided I needed to have a working app first. 😅) -```bash -npm i && code . && npm run dev -``` +I did request some help from ChatGPT for making the function to display the time lapsed since a happy thought was posted, but after looking at the code I felt like I could have come up with the function on my own. -### The Problem +One challenge I didn't expect was how to get the accessibility score in Lighthouse to be 95 or higher when the design showed a light gray text on a white background. I tried making the font bigger and bolder, but would still be just under the accesibility score requirement. In the end, I decided accessibility was a higher priority than copying the design perfectly, so I had to settle for a darker gray font color. -Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? +If I had more time, I would love to try to tackle the stretch goals, like triggering an animation when submitting a new thought. ### View it live -Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. - -## Instructions - - - See instructions of this project - +https://happy-thoughts-joyce.netlify.app/ diff --git a/src/App.jsx b/src/App.jsx index 1091d431..6575d200 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,3 +1,68 @@ +import { useState, useEffect } from 'react'; +import SubmitForm from './components/SubmitForm'; +import { HappyThought } from './components/HappyThought'; + +const URL = 'https://happy-thoughts-ux7hkzgmwa-uc.a.run.app/thoughts'; + export const App = () => { - return
Find me in src/app.jsx!
; + const [thoughts, setThoughts] = useState([]); + + // Fetch the most recent thoughts + useEffect(() => { + const fetchThoughts = async () => { + try { + const response = await fetch(URL); + const result = await response.json(); + setThoughts(result); // Sets the array of 20 latest thoughts + } catch (error) { + console.error('Error fetching thoughts:', error); + } + }; + fetchThoughts(); + }, []); + + // Adds a new thought to the list + const addThought = (newThought) => { + setThoughts((prevThoughts) => [newThought, ...prevThoughts.slice(0, 19)]); + }; + + // Like a thought + const handleLike = async (thoughtId) => { + const likeURL = `${URL}/${thoughtId}/like`; + try { + const response = await fetch(likeURL, { + method: 'POST', + }) + if (response.ok) { + // Update hearts count for liked thought + setThoughts((prevThoughts) => + prevThoughts.map((thought) => + thought._id === thoughtId ? { ...thought, hearts: thought.hearts + 1 } : thought + ) + ); + } + } catch (error) { + console.error('Error liking the thought:', error); + } + }; + + return ( +
+

Happy Thoughts 😊

+
+ +
+ {thoughts.map((thought) => ( + + ))} +
+
+
+ ); }; + +export default App; diff --git a/src/components/HappyThought.jsx b/src/components/HappyThought.jsx new file mode 100644 index 00000000..b6c2bd1a --- /dev/null +++ b/src/components/HappyThought.jsx @@ -0,0 +1,63 @@ +import { useState, useEffect } from 'react'; + +// Function to calculate time difference +const timeAgo = (createdAt) => { + const now = new Date(); + const createdTime = new Date(createdAt); + const timeDiff = now - createdTime; + + const seconds = Math.floor(timeDiff / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + const days = Math.floor(hours / 24); + + if (days > 0) { + return `${days} day${days > 1 ? 's' : ''} ago`; + } else if (hours > 0) { + return `${hours} hour${hours > 1 ? 's' : ''} ago`; + } else if (minutes > 0) { + return `${minutes} minute${minutes > 1 ? 's' : ''} ago`; + } else { + return `${seconds} second${seconds > 1 ? 's' : ''} ago`; + } +}; + +export const HappyThought = ({ thought, onLike }) => { + // Track how long ago the thought was posted + const [timeSincePosted, setTimeSincePosted] = useState(timeAgo(thought.createdAt)); + + // Track whether the heart button has been clicked + const [isClicked, setIsClicked] = useState(false); + + // Update time difference every minute + useEffect(() => { + const interval = setInterval(() => { + setTimeSincePosted(timeAgo(thought.createdAt)); + }, 60000); // Update every 60 seconds + return () => clearInterval(interval); + }, [thought.createdAt]); + + const handleLikeClick = () => { + if (!isClicked) { + onLike(thought._id); // Trigger like functionality + setIsClicked(true); // Set the clicked state to true to persist the clicked state + } + }; + + + return ( +
+

{thought.message}

+
+ + x {thought.hearts} + {timeSincePosted} +
+
+ ); +}; + + diff --git a/src/components/SubmitForm.jsx b/src/components/SubmitForm.jsx new file mode 100644 index 00000000..6a82db46 --- /dev/null +++ b/src/components/SubmitForm.jsx @@ -0,0 +1,60 @@ +import { useState } from 'react'; +import PropTypes from 'prop-types'; + +export const SubmitForm = ({ onSubmit }) => { + + const [thought, setThought] = useState(''); + const [error, setError] = useState(''); + + const handleSubmit = async (event) => { + event.preventDefault(); + + const URL = 'https://happy-thoughts-ux7hkzgmwa-uc.a.run.app/thoughts'; + + // Validate message length + if (thought.length < 5 || thought.length > 140) { + setError('Message must be between 5 and 140 characters.'); + return; + } + + try { + const res = await fetch(URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ message: thought }), + }); + const newThought = await res.json(); + onSubmit(newThought); // Passes new thought to App.jsx + setThought(''); // Clears input field + setError(''); // Clears any previous errors + } catch (error) { + console.log('Error posting thought:', error); + } + }; + + return ( +
+ +