-
Notifications
You must be signed in to change notification settings - Fork 145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Happy Thoughts Project #96
Open
gittebe
wants to merge
27
commits into
Technigo:main
Choose a base branch
from
gittebe:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
923f8c7
- added the components Header, HappyThought, ThoughtForm and ThoughtList
gittebe 95808ff
-worked on CSS
gittebe c213610
- added a submit button component,
gittebe 0b98bf3
- added like LikeButton
gittebe 7dd735c
- added function to update likes
gittebe bbe3f36
- fixed a bug by replacing two prop names
gittebe 58af9d4
- added the time component
gittebe 0cd9b8a
- restructured the components by adding two new js files fot the hook…
gittebe d57a9c4
- worked on the CSS
gittebe de22a47
- added a @media for the submit button
gittebe 83c84e9
-changed the color of the time and hearts counter to make them access…
gittebe 17d1008
- worked on CSS
gittebe 593d84c
- added netlify link
gittebe 35f8b7b
- added character Counter
gittebe 5378baf
- toggle for the like button
gittebe 334bda4
- added an animation
gittebe cf1e325
- max-length of characters changed
gittebe b20f2e2
- worked on the like function
gittebe 0f57ab5
- worked on the like button
gittebe 0475f75
- I tidied up the code to improve readability
gittebe 7fcdf6a
- cleaned code and added README
gittebe 22c4f25
- changed README
gittebe f662baa
- connected to happy-thoughts-api backend
gittebe 856c707
- changed api
gittebe 98eaed6
- changed api
gittebe b0fb28f
- added netlify link
gittebe b7c2f65
- fixed url
gittebe File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,40 @@ | ||
<h1 align="center"> | ||
<a href=""> | ||
<img src="/src/assets/happy-thoughts.svg" alt="Project Banner Image"> | ||
</a> | ||
</h1> | ||
# Happy Thoughts App | ||
This project is a React-based application that allows users to share their happy thoughts with others. Users can submit short messages (thoughts), and view a list of all submitted thoughts along with their like counts. The app also includes features such as validation for thought submission, a character counter, and a fun heart animation when submitting thoughts. | ||
|
||
# Happy thoughts Project | ||
Key Features: | ||
Users can enter their thoughts in a form and submit them to the app. | ||
Thoughts are limited to a maximum of 140 characters and must be at least 5 characters long. | ||
On successful submission, the thought is added to the list of thoughts, and the input is cleared. | ||
|
||
In this week's project, you'll be able to practice your React state skills by fetching and posting data to an API. | ||
Like Thoughts: Each thought has a like button that allows users to "like" a thought by clicking on a heart icon. The number of likes (hearts) is updated both locally and on the backend. | ||
Users can "unlike" a thought by clicking the heart again. | ||
|
||
## Getting Started with the Project | ||
Local storage is used to persist liked thoughts across page reloads. | ||
|
||
### Dependency Installation & Startup Development Server | ||
Heart Animation: A heart animation appears when a user submits a new thought, providing a delightful visual confirmation. | ||
|
||
Once cloned, navigate to the project's root directory and this project uses npm (Node Package Manager) to manage its dependencies. | ||
Character Count: As users type their thought, a character counter is displayed to show how many characters remain before reaching the 140-character limit. | ||
|
||
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. | ||
Validation messages appear if the user attempts to submit a thought that doesn't meet the requirements. | ||
|
||
```bash | ||
npm i && code . && npm run dev | ||
``` | ||
Time Ago: | ||
Each thought is tagged with the time it was submitted (e.g., "3 minutes ago", "2 days ago"). | ||
The time difference updates every minute to reflect the most accurate "time ago" format. | ||
|
||
### The Problem | ||
Responsive Design: The app is responsive and works well on mobile and desktop screens, ensuring a smooth experience across different devices. | ||
|
||
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? | ||
# Tech Stack | ||
React: Used for creating interactive UI components, managing state, and handling events. | ||
|
||
### View it live | ||
CSS: Custom styling is used to design a clean and user-friendly interface. | ||
|
||
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. | ||
Local Storage: Local storage is utilized to remember liked thoughts, even after page refreshes. | ||
|
||
## Instructions | ||
PropTypes: Ensures the correctness of props passed to React components. | ||
|
||
<a href="instructions.md"> | ||
See instructions of this project | ||
</a> | ||
API Integration: API calls are made to store and fetch thoughts from a backend server (mocked with functions postThought and likeThought). | ||
|
||
Hooks: The app utilizes custom hooks (useState, useEffect, useFetchThoughts) for managing state and side effects. | ||
|
||
## view it live | ||
https://post-happy-thoughts.netlify.app/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,15 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Happy Thought - Project - Week 7</title> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
<script type="module" src="/src/main.jsx"></script> | ||
</body> | ||
</html> | ||
|
||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Happy Thought - Project - Week 7</title> | ||
</head> | ||
|
||
<body> | ||
<div id="root"></div> | ||
<script type="module" src="/src/main.jsx"></script> | ||
</body> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,11 @@ | ||
import { HappyThoughts } from "./components/HappyThoughts"; | ||
import { HappyThoughtsHeader } from "./components/Header"; | ||
|
||
export const App = () => { | ||
return <div>Find me in src/app.jsx!</div>; | ||
}; | ||
return ( | ||
<> | ||
<HappyThoughtsHeader /> | ||
<HappyThoughts /> | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// const BASE_URL = "https://happy-thoughts-ux7hkzgmwa-uc.a.run.app/thoughts" | ||
|
||
const BASE_URL = "https://project-happy-thoughts-api-production-5d1d.up.railway.app/thoughts" | ||
|
||
// fetching data from the API | ||
export const fetchThoughts = async () => { | ||
const response = await fetch(BASE_URL) | ||
return response.json() | ||
} | ||
|
||
// function to send the message as a JSON-object to the url via POST | ||
export const postThought = async (message) => { | ||
const response = await fetch(BASE_URL, { | ||
method: "POST", | ||
headers: { "Content-Type": "application/json" }, | ||
body: JSON.stringify({ message }) | ||
}) | ||
return response.ok | ||
} | ||
|
||
// function to send a POST request to the server to like a message/thought | ||
export const likeThought = async (thoughtId) => { | ||
const response = await fetch(`${BASE_URL}/${thoughtId}/like`, { method: "POST" }) | ||
if (!response.ok) throw new Error("Failed to like thought") | ||
} | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { useState } from "react"; | ||
import { ThoughtsForm } from "./form/ThoughtForm"; | ||
import { ThoughtList } from "./list/ThoughtList"; | ||
import { useFetchThoughts } from "../hooks"; | ||
import { postThought, likeThought } from "../api"; | ||
|
||
export const HappyThoughts = () => { | ||
const [newThought, setNewThought] = useState("") | ||
const { thoughts, setThoughts, loading, getThoughts } = useFetchThoughts() | ||
|
||
// this function handles the submission of the thoughts form. | ||
// It prevents the default form submission behavior, checks if the input is not empty and then calls the postThought functions to send the new thought to the API. If the post is successful, it clears the input and refreshes the list of the thoughts (getThoughts) | ||
const handleFormSubmit = async (e) => { | ||
e.preventDefault() | ||
if (!newThought.trim()) return | ||
const success = await postThought(newThought) | ||
if (success) { | ||
setNewThought("") | ||
getThoughts() | ||
} | ||
} | ||
|
||
// this function handles the likes of the thought by its ID. It calls the function likeThought, and if successful, updates the local state with the new number of hearts by mapping over the previous thoughts | ||
const handleLike = async (thoughtId, isClicked) => { | ||
try { | ||
await likeThought(thoughtId) | ||
setThoughts((prevThoughts) => | ||
prevThoughts.map((thought) => | ||
thought._id === thoughtId ? { ...thought, hearts: thought.hearts + (isClicked ? 1 : -1) } : thought | ||
)) | ||
} catch (error) { | ||
console.error("Error liking thought", error) | ||
} | ||
} | ||
|
||
return ( | ||
<div className="content"> | ||
<ThoughtsForm | ||
newThought={newThought} | ||
setNewThought={setNewThought} | ||
handleFormSubmit={handleFormSubmit} | ||
/> | ||
{loading ? <p>Loading...</p> : <ThoughtList thoughts={thoughts} onLike={handleLike} />} | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export const HappyThoughtsHeader = () => { | ||
return ( | ||
<header> | ||
<div className='header-container'> | ||
<h1>Uplifting Moments</h1> | ||
<h2>Share, Like, and Celebrate What Makes Life Beautiful</h2> | ||
</div> | ||
</header> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import PropTypes from "prop-types"; | ||
import "./animation.css"; | ||
|
||
export const HeartAnimation = ({ isVisible }) => { | ||
return ( | ||
<> | ||
{isVisible && ( | ||
<div className="heart-animation"> | ||
❤️ | ||
</div> | ||
)} | ||
</> | ||
) | ||
} | ||
|
||
HeartAnimation.propTypes = { | ||
isVisible: PropTypes.bool.isRequired, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import PropTypes from "prop-types"; | ||
import "./form.css"; | ||
import { SubmitButton } from "./submitButton/SubmitButton"; | ||
import { CharacterCounter } from "./characterCounter/CharacterCounter"; | ||
import { useState } from "react"; | ||
import { HeartAnimation } from "./Animation"; | ||
|
||
export const ThoughtsForm = ({ newThought, setNewThought, handleFormSubmit }) => { | ||
const maxChars = 140 | ||
const minChars = 5 | ||
const [error, setError] = useState("") | ||
const [showHeart, setShowHeart] = useState(false) | ||
|
||
const handleInputChange = (e) => { | ||
setNewThought(e.target.value) | ||
setError("") | ||
} | ||
|
||
const handleSubmit = (e) => { | ||
e.preventDefault() | ||
// Validation checks | ||
if (newThought.length === 0) { | ||
setError("The message cannot be empty."); | ||
} else if (newThought.length < minChars) { | ||
setError(`The message must be at least ${minChars} characters.`); | ||
} else if (newThought.length > maxChars) { | ||
setError(`The message cannot exceed ${maxChars} characters.`); | ||
} else { | ||
setError("") | ||
setShowHeart(true) | ||
handleFormSubmit(e) | ||
setNewThought("") | ||
setTimeout(() => { | ||
setShowHeart(false) | ||
}, 2000); | ||
} | ||
} | ||
|
||
return ( | ||
<> | ||
<div className="form-container"> | ||
<p>What is making you happy right now?</p> | ||
<form onSubmit={handleSubmit}> | ||
<textarea | ||
type="text" | ||
value={newThought} | ||
onChange={handleInputChange} | ||
placeholder="Type here..." | ||
maxLength="maxChars" | ||
/> | ||
<section className="counter-container"> | ||
<CharacterCounter currentLength={newThought.length} maxChars={maxChars} /> | ||
</section> | ||
{error && <p className="error-message">{error}</p>} | ||
<section className="submit-button-container"> | ||
<SubmitButton /> | ||
</section> | ||
</form> | ||
</div> | ||
<HeartAnimation isVisible={showHeart} /> | ||
</> | ||
) | ||
} | ||
|
||
// Props Validation | ||
ThoughtsForm.propTypes = { | ||
newThought: PropTypes.string.isRequired, | ||
setNewThought: PropTypes.func.isRequired, | ||
handleFormSubmit: PropTypes.func.isRequired, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
.heart-animation { | ||
position: fixed; | ||
top: 50%; | ||
left: 50%; | ||
transform: translate(-50%, -50%); | ||
font-size: 200px; | ||
color: red; | ||
animation: pulse 0.6s ease-in-out forwards, scale 0.6s ease-in-out forwards; | ||
pointer-events: none; | ||
z-index: 1000; | ||
} | ||
|
||
@keyframes pulse { | ||
0% { | ||
transform: translate(-50%, -50%) scale(1); | ||
} | ||
|
||
50% { | ||
transform: translate(-50%, -50%) scale(1.2); | ||
} | ||
|
||
100% { | ||
transform: translate(-50%, -50%) scale(1); | ||
} | ||
} | ||
|
||
@keyframes scale { | ||
0% { | ||
opacity: 1; | ||
} | ||
|
||
100% { | ||
opacity: 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import PropTypes from "prop-types"; | ||
import "./characterCounter.css"; | ||
|
||
export const CharacterCounter = ({ currentLength, maxChars }) => { | ||
const charsLeft = maxChars - currentLength | ||
const isOverLimit = currentLength > 140 | ||
|
||
return ( | ||
<div className={`character-count ${isOverLimit ? "warning" : ""}`}> | ||
{charsLeft} characters left | ||
</div> | ||
) | ||
} | ||
|
||
CharacterCounter.propTypes = { | ||
currentLength: PropTypes.number.isRequired, | ||
maxChars: PropTypes.number.isRequired, | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I really liked this counter and it worked like a charm too! It gives the app even more of an old-school vibe, which I love. The isOverLimit logic is clear and easy to follow. Great job! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
.counter-container { | ||
display: flex; | ||
justify-content: end; | ||
width: 90%; | ||
} | ||
|
||
.character-count { | ||
font-weight: bold; | ||
} | ||
|
||
.character-count.warning { | ||
color: red; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Smart to make the API functions standalone! That makes it easier to reuse them across other components since you don't have to duplicate the code.