Skip to content
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

Project Happy Thoughts - Hannah Ek #435

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions code/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"babel-eslint": "^10.1.0",
"date-fns": "^2.29.3",
"eslint": "^8.21.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-plugin-import": "^2.26.0",
Expand Down
5 changes: 4 additions & 1 deletion code/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta property="og:title" content="Project Happy Thoughts - Hannah Ek"> <!-- OG (open graph) tags are snippets of code that provide meta data about a web page, mainly to social media platforms (ex facebook, linkedin, instagram)-->
<meta property="og:description" content="Project Happy Thoughts, a project by Hannah Ek, a student @Technigo Web Development Bootcamp spring 2023">
<meta property="og:image" content="https://i.postimg.cc/kMzq5ttr/Screenshot-2023-03-26-at-09-19-59.png">
Comment on lines +7 to +9
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job including the og tags! I suggest you check what it looks like through this page: https://www.opengraph.xyz/url/https%3A%2F%2Fproject-happy-thoughts-hannah-ek.netlify.app%2F

I think it would look even better if you make the image wider with white space, then more of it will be shown and not "cut".

<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Expand All @@ -13,7 +16,7 @@
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`.
-->
<title>Technigo React App</title>
<title>Project Happy Thoughts</title>
</head>

<body>
Expand Down
17 changes: 12 additions & 5 deletions code/src/App.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import React from 'react';
import { ThoughtFeed } from 'components/ThoughtFeed';
import { NewThought } from 'components/NewThought';

export const App = () => {
return (
<div>
Find me in src/app.js!
</div>
);
}
<>
<section>
<NewThought />
</section>
<section>
<ThoughtFeed />
</section>
</>
Comment on lines +7 to +14
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you look in the inspector, the

seem to not be needed here. I think you could rewrite it like this:

<> <NewThought /> <ThoughtFeed /> </>

)
}
51 changes: 51 additions & 0 deletions code/src/components/NewThought.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { useState } from 'react';

export const NewThought = () => {
const [newThought, setNewThought] = useState('');

const HandleFormSubmit = (event) => {
event.preventDefault();
if (newThought.length < 5) {
return alert('Get on that keyboard, more than 5 characters, please!')
} else if (newThought.length > 140) {
return alert('Are you some sort of hacker?! No more than 140 characters, please!')
} else {
Comment on lines +8 to +12
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love the messages haha! Also great and condense way of writing the error conditionals.

const Submit = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: newThought })
};
fetch('https://happy-thoughts-ux7hkzgmwa-uc.a.run.app/thoughts', Submit)
.then((response) => response.json())
.then(() => {
setNewThought('');
setTimeout(() => window.location.reload(), 3000)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really great idea to use the timed function here!

})
}
};

return (
<section className="main-container">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that you re-used the className for both the NewThought section and the ThoughtFeed section. Great way of keeping down the amount of code! It shows that you have really thought it through :)

<form onSubmit={HandleFormSubmit} className="form-card">
<label htmlFor="newThought" className="head-thought">What is making you happy right now?
<textarea
id="newThought"
type="text"
placeholder="Type your happy thoughts here..."
rows="4"
cols="40"
value={newThought}
onChange={(event) => setNewThought(event.target.value)} />
</label>
<p className={newThought.length > 140 ? 'counter' : 'counter'}>{newThought.length} / 140</p>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also a comment regarding the speak-reader. Now it never reads the "/", which makes it confusing to understand the length that is written and allowed. I recommend adding an aria-label to this section.

<p className={newThought.length > 140 ? 'counter' : 'counter'} aria-label={newThought.length} out of 140>{newThought.length} / 140</p>

<button className="submitBtn" type="submit">
<span>
<span className="heart" role="img" aria-label="heart"> 💗 </span>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice with the aria label! Sounded good when I listened to it.

Send happy thought
<span className="heart" role="img" aria-label="heart"> 💗 </span>
</span>
</button>
</form>
</section>
)
};
11 changes: 11 additions & 0 deletions code/src/components/Spinning.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react'

const Spinning = () => (
<div className="loading-thoughts">
<div className="loading-spinner">
💖
</div>
</div>
Comment on lines +3 to +8
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This thing looks great! Nice job!

)

export default Spinning;
55 changes: 55 additions & 0 deletions code/src/components/ThoughtFeed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* eslint-disable no-underscore-dangle */
import React, { useState, useEffect } from 'react';
import { formatDistance } from 'date-fns';
import Spinning from './Spinning';
// import { Loading } from './Loading'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this? :)


const API = 'https://happy-thoughts-ux7hkzgmwa-uc.a.run.app/thoughts'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice idea to have the fetch adress outside the function, makes it look a lot cleaner inside the fetch!


export const ThoughtFeed = () => {
const [thoughtsList, setThoughtsList] = useState([]);
const [loading, setLoading] = useState(false);

useEffect(() => {
setLoading(true);
fetch(`${API}`)
.then((res) => res.json())
.then((data) => setThoughtsList(data))
.catch((error) => console.log(error))
.finally(() => { setLoading(false) })
}, []);

const HandleLike = (thoughtId) => {
fetch(`${API}/${thoughtId}/like`, { method: 'POST' })
.then((res) => res.json())
.then((data) => {
const UpdateHearts = thoughtsList.map((like) => {
if (like._id === data._id) {
like.hearts += 1
return like
} else { return like }
})
setThoughtsList(UpdateHearts)
})
};

return (
<>
<section className="main-container">
{!loading && thoughtsList.map((thought) => {
return (
<div key={thought._id} className="card">
<p className="post-text">{thought.message}</p>
<button type="button" className={thought.hearts === 0 ? 'noLikesBtn' : 'likesBtn'} onClick={() => HandleLike(thought._id)}>
<span role="img" aria-label="Like this post">💗</span>
</button>
<span className="sum-hearts">x {thought.hearts}</span>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I listened to your app and this sounds a bit funny, it really says e.g. "x four". Could you maybe add an aria-label to it, so that the speaker reads "likes times four" or similar?

<span className="sum-hearts" aria-label={likes times ${thought.hearts}}> x {thought.hearts} </span>

<p className="date"> {formatDistance(new Date(thought.createdAt), Date.now(), { addSuffix: true })}</p>
</div>
)
})}
</section>
{loading && (<Spinning />)}
</>
)
};
169 changes: 169 additions & 0 deletions code/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,178 @@ body {
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}

code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}

.main-container {
padding: 0 18px;
box-sizing: border-box;
width: 400px;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixed width could easily be changed into a more responsive one I think if you'd like that. It looks good for desktop and tablets, but the smallest phone sizes (320px) get a side scroll.

margin: 0 auto;
}

.form-card {
box-sizing: border-box;
background-color: #f0f0f0f3;
margin: 20px 0;
padding: 15px;
border: 1px solid #000000;
box-shadow: 4px 4px #000000;
}

.head-thought {
font-family: "Courier New";
font-weight: bold;
font-size: 15px;
}

textarea {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To prevent users from resizing the textarea by dragging the corner, you can use the resize property and set its value to none. This will disable the resizing feature of the textarea.

resize: none;

border: none;
color: #000;
padding: 8px;
margin: 15px 0 10px;
width: 100%;
box-sizing: border-box;
}

.submitBtn {
font-family: "Courier New";
font-size: 12px;
font-weight: bold;
border: none;
border-radius: 50px;
color: #380118;
background-color: #ffadad;
padding: 10px 20px;
cursor: pointer;
margin-left: 45px;
}

.heart {
margin: 8px;
}

.img {
margin: 0px 2px;
}

.card {
box-sizing: border-box;
background-color: rgb(255, 255, 255);
border: 1px solid #000000;
margin: 18px 0;
padding: 10px 20px;
box-shadow: 4.5px 5px #000000;
overflow-wrap: break-word;
font-weight: bold;
}

.post-text {
font-family: "Courier New";
font-size: 13px;
color: black;
margin: 0;
padding: 18px 0;
min-height: 20px;
}

.heartEmo {
font-size: 12px;
transition: font-size 0.1s ease-in-out;
}

.heartEmo:hover {
cursor: pointer;
font-size: 18px;
}
Comment on lines +92 to +100
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find "heartEmo" anywhere other than in the css file. Is it supposed to be named something else?

Also, " cursor:pointer; " can be added directly to the non-hover property, since it will be the same outcome :)


.hearts {
margin-right: 10px;
border: none;
border-radius: 50%;
height: 38px;
width: 38px;
cursor: pointer;
background-color: #e2016a51;
}

.likesBtn,
.noLikesBtn {
background-color: #ffacac;
margin-right: 8px;
border: none;
border-radius: 55%;
height: 32px;
width: 32px;
cursor: pointer;
}

.noLikesBtn {
background-color: #f3f3f3a4;
}

.sum-hearts,
.counter {
font-size: 12px;
color: #7d7a7af3;
text-align: right;
margin: 0;
position: absolute;
margin-top: 10px;
}

.date {
float: right;
position: relative;
font-size: 12px;
color: #7d7a7af3;
text-align: right;
}

.loading-spinner {
animation-name: loadspinner;
display: block;
width: 40px;
height: 40px;
font-size: 40px;
margin-top: 30px;
margin: 30px auto;
animation-duration: 800ms;
animation-iteration-count: infinite;
animation-timing-function: linear;
}

@keyframes loadspinner {
from {
transform: rotate(0deg)

}
to {
transform: rotate(360deg)
}
}



/*--MEDIA QUERIES--*/

@media (min-width: 768px) {
.mainContainer {
width: 450px;
}
}

@media (min-width: 1024px) {
.mainContainer {
width: 500px;
}
}
Loading