-
Notifications
You must be signed in to change notification settings - Fork 434
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
Week 7 - Happy Thoughts by Daniel #442
base: master
Are you sure you want to change the base?
Changes from all commits
e8ac313
c6e4b06
7bb7e9e
91fa3a5
434f355
c467f04
c0f91ea
8fe2e76
bc799e3
f028ed5
91202ed
506dc87
3b571ab
dc8ec55
6ec6f46
88eee78
ab860de
2a9d9b2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,27 @@ | ||
# Happy Thoughts | ||
|
||
Replace this readme with your own information about your project. | ||
|
||
Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. | ||
A happy twitter-like wall for all you thoughts. Share what you are thinking about right now! | ||
|
||
## The problem | ||
|
||
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? | ||
### The challenge | ||
|
||
|
||
In this week's project, you'll be able to practice your React state skills by fetching and posting data to an API. | ||
|
||
**What you need to do** | ||
|
||
✓ Your page should follow the design as closely as possible | ||
|
||
✓ You should list the most recent thoughts at the top and older thoughts at the bottom (sorted) | ||
|
||
✓ Your thoughts should show the content of the message and how many likes they've received | ||
|
||
✓ You should have a form to post new thoughts | ||
|
||
✓ You should implement the heart button to send likes on a 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. | ||
https://technigo-happy-thoughts-app.netlify.app/ |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,58 @@ | ||
import React from 'react'; | ||
/* eslint-disable react/jsx-closing-bracket-location */ | ||
/* eslint-disable no-unused-vars */ | ||
import React, { useEffect, useState } from 'react'; | ||
import { List } from 'components/List'; | ||
import { Input } from 'components/Input'; | ||
|
||
export const App = () => { | ||
const [thoughts, setThoughts] = useState([]); | ||
const [newPost, setNewPost] = useState(''); | ||
const [loading, setLoading] = useState(true); | ||
|
||
const fetchThoughts = () => { | ||
fetch('https://project-happy-thoughts-api-hgwjnnqcva-lz.a.run.app/thoughts') | ||
.then((res) => res.json()) | ||
.then((data) => { | ||
setThoughts(data); | ||
}) | ||
.finally(() => setLoading(false)) | ||
.catch((error) => console.log(error)); | ||
}; | ||
|
||
useEffect(() => { | ||
fetchThoughts(); | ||
}, []); | ||
|
||
const handleLikeChange = (id) => { | ||
const options = { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json' | ||
} | ||
}; | ||
fetch( | ||
`https://project-happy-thoughts-api-hgwjnnqcva-lz.a.run.app/thoughts/${id}/like`, | ||
options | ||
) | ||
.catch((error) => console.log(error)) | ||
.finally(() => fetchThoughts()); | ||
}; | ||
return ( | ||
<div> | ||
Find me in src/app.js! | ||
<div className="container"> | ||
<Input | ||
thoughts={thoughts} | ||
setThoughts={setThoughts} | ||
newPost={newPost} | ||
setNewPost={setNewPost} | ||
/> | ||
{loading && <p>Thoughts is loading...</p>} | ||
{thoughts && ( | ||
<List | ||
thoughts={thoughts} | ||
setThoughts={setThoughts} | ||
handleLikeChange={handleLikeChange} | ||
/> | ||
)} | ||
</div> | ||
); | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
.box { | ||
background-color: rgb(250, 255, 253); | ||
width: 80%; | ||
display: flex; | ||
flex-wrap: wrap; | ||
border: 2px solid black; | ||
box-shadow: 10px 10px 5px 0px rgba(0, 0, 0, 0.75); | ||
-webkit-box-shadow: 10px 10px 5px 0px rgba(0, 0, 0, 0.75); | ||
-moz-box-shadow: 10px 10px 5px 0px rgba(0, 0, 0, 0.75); | ||
margin: 2rem; | ||
} | ||
|
||
.input-box { | ||
background-color: rgb(234, 233, 233); | ||
} | ||
|
||
.input-box * { | ||
padding: 0 1rem 0 1rem; | ||
} | ||
|
||
label { | ||
width: 100%; | ||
font-size: 1.2rem; | ||
font-weight: 700; | ||
margin: 1rem 0 0 0; | ||
} | ||
|
||
textarea { | ||
width: 100%; | ||
margin: 0.5rem 0 0 0; | ||
border: 1px solid gray; | ||
background-color: rgb(254, 254, 254); | ||
} | ||
|
||
.input-box button { | ||
height: 3rem; | ||
font-weight: 700; | ||
background-color: rgba(229, 3, 3, 0.5); | ||
border: none; | ||
border-radius: 20px; | ||
margin: 0.5rem 1rem 1rem; | ||
} | ||
button:hover { | ||
cursor: pointer; | ||
} | ||
|
||
.input-box button:focus { | ||
border: 2px solid black; | ||
} | ||
button:disabled, | ||
button[disabled] { | ||
border: 1px solid #999999; | ||
background-color: #cccccc; | ||
color: #666666; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/* eslint-disable react/jsx-closing-bracket-location */ | ||
/* eslint-disable no-unused-vars */ | ||
import React from 'react'; | ||
import './Input.css'; | ||
|
||
export const Input = ({ newPost, setNewPost, thoughts, setThoughts }) => { | ||
const handleFormSubmit = (event) => { | ||
event.preventDefault(); | ||
const options = { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json' | ||
}, | ||
body: JSON.stringify({ message: newPost }) | ||
}; | ||
|
||
fetch( | ||
'https://project-happy-thoughts-api-hgwjnnqcva-lz.a.run.app/thoughts', | ||
options | ||
) | ||
.then((res) => res.json()) | ||
.then((newThought) => { | ||
setThoughts((prevThoughts) => [newThought, ...prevThoughts]); | ||
}) | ||
.finally(() => { | ||
setNewPost(''); | ||
}) | ||
.catch((error) => console.log(error)); | ||
}; | ||
return ( | ||
<div> | ||
<form onSubmit={handleFormSubmit} className="box input-box"> | ||
<label htmlFor="thoughts" className="input-heading"> | ||
Whats making you happy right now? | ||
<textarea | ||
name="thoughts" | ||
rows="4" | ||
value={newPost} | ||
placeholder="Write something nice" | ||
onChange={(e) => { | ||
setNewPost(e.target.value); | ||
}} | ||
/> | ||
</label> | ||
<button | ||
type="submit" | ||
disabled={newPost.length < 5 || newPost.length > 140} | ||
> | ||
{' '} | ||
❤️ Send happy thought ❤️ | ||
</button> | ||
</form> | ||
</div> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;500;600&display=swap'); | ||
.box { | ||
background-color: rgb(250, 255, 253); | ||
width: 80%; | ||
display: flex; | ||
flex-wrap: wrap; | ||
border: 2px solid black; | ||
box-shadow: 10px 10px 5px 0px rgba(0, 0, 0, 0.75); | ||
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. The softened box-shadow looks nice |
||
-webkit-box-shadow: 10px 10px 5px 0px rgba(0, 0, 0, 0.75); | ||
-moz-box-shadow: 10px 10px 5px 0px rgba(0, 0, 0, 0.75); | ||
margin: 2rem; | ||
} | ||
|
||
.list { | ||
width: 80%; | ||
} | ||
.message-text { | ||
font-family: 'Roboto Mono', monospace; | ||
width: 100%; | ||
font-size: 1.2rem; | ||
font-weight: 700; | ||
padding: 0 0 0 1rem; | ||
overflow-wrap: break-word; | ||
} | ||
.stats-container { | ||
width: 100%; | ||
display: flex; | ||
justify-content: space-between; | ||
margin: 1rem; | ||
} | ||
|
||
.like-container { | ||
display: flex; | ||
justify-content: space-between; | ||
width: 5rem; | ||
} | ||
.like-container p { | ||
margin: auto; | ||
} | ||
.heart { | ||
border-radius: 50%; | ||
width: 50px; | ||
height: 50px; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
background-color: rgb(253, 254, 254); | ||
} | ||
.heart:hover { | ||
cursor: pointer; | ||
background-color: rgba(255, 0, 0, 0.2); | ||
} | ||
|
||
.time { | ||
display: flex; | ||
} | ||
|
||
.time p { | ||
margin: auto; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/* eslint-disable jsx-a11y/click-events-have-key-events */ | ||
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ | ||
/* eslint-disable no-unused-vars */ | ||
/* eslint-disable no-underscore-dangle */ | ||
import React from 'react'; | ||
import moment from 'moment'; | ||
import './List.css'; | ||
|
||
export const List = ({ thoughts, setThoughts, handleLikeChange }) => { | ||
return ( | ||
<div className="list"> | ||
<ul> | ||
{thoughts.map((thought) => ( | ||
<div key={thought._id} className="box messages"> | ||
<p className="message-text">{thought.message}</p> | ||
<div className="stats-container"> | ||
<div className="like-container"> | ||
<div className="heart"> | ||
<p onClick={() => handleLikeChange(thought._id)}>❤️</p> | ||
</div> | ||
<p>x{thought.heart}</p> | ||
</div> | ||
<div className="time"> | ||
<p>{moment(thought.createdAt).fromNow()}</p> | ||
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 got posted "in a few seconds" when I posted a thought, so this way of handling the post time seems to have some vulnerabilities. Unless you made some sort of time machine, which is awesome, and in that case: carry on |
||
</div> | ||
</div> | ||
</div> | ||
))} | ||
</ul> | ||
</div> | ||
); | ||
}; |
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 way of handling the allowed input length!