Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
Expand All @@ -10,4 +10,4 @@
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
</html>
34 changes: 31 additions & 3 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,42 @@
import './App.css';
import chatdata from './data/messages.json';
import { useState } from 'react';
import ChatLog from './components/Chatlog';
//import ChatEntry from './components/ChatEntry';


const App = () => {
const [chatData, setChats] = useState(chatdata);
const toggleLike = (id) => {
setChats(chatData =>
chatData.map(chat =>{
if (chat.id === id) {
return{ ...chat, liked: !chat.liked };
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
return{ ...chat, liked: !chat.liked };
return { ...chat, liked: !chat.liked };

} else {
return chat;
}
}
)
);
};

const totalLikes = () => {
return chatData.reduce((count, chat) => {
return chat.liked ? count + 1 : count;
}, 0);
};
Comment on lines +23 to +27
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Great work on this function implementation, @Bipentium25. Using the .reduce method is a very JS idiomatic way to code this logic.


const local = chatData[0].sender;
const remote = chatData.find(chat => chat.sender !== local).sender;
Comment on lines +29 to +30
Copy link
Copy Markdown

Choose a reason for hiding this comment

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


return (
<div id="App">
<header>
<h1>Application title</h1>
<h1>Chat Between {local} and {remote}</h1>
<section><h2 className='widget'>{totalLikes()} ❤️s</h2></section>
Comment on lines +35 to +36
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

👍🏿

</header>
<main>
{/* Wave 01: Render one ChatEntry component
Wave 02: Render ChatLog component */}
<ChatLog entries={chatData} toggleLike={toggleLike} local={local}/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nice! I see that you leveraged a local prop inside of your Chatlog component to mark all of your entry objects with a matching sender as the local messages.

</main>
</div>
);
Expand Down
28 changes: 19 additions & 9 deletions src/components/ChatEntry.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import './ChatEntry.css';
import PropTypes from 'prop-types';
import TimeStamp from './TimeStamp';

const ChatEntry = () => {
const ChatEntry = ({ id, sender, body, timeStamp, liked, toggleLike = () => {}, isLocal = false }) => {
const heart = liked ? '❤️' : '🤍';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Perfect use case for a ternary operator! This is how we implement conditional rendering.

return (
// Replace the outer tag name with a semantic element that fits our use case
<replace-with-relevant-semantic-element className="chat-entry local">
<h2 className="entry-name">Replace with name of sender</h2>
<section className="entry-bubble">
<p>Replace with body of ChatEntry</p>
<p className="entry-time">Replace with TimeStamp component</p>
<button className="like">🤍</button>
<div className={`chat-entry ${isLocal ? 'local' : 'remote'}`}>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Another great use of ternary operators!

<h2 className='entry-name'>{sender}</h2>
<section className='entry-bubble'>
<p>{body}</p>
<p className='entry-time'><TimeStamp time={timeStamp}></TimeStamp></p>
Comment on lines +10 to +13
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

👌

<button className='like' onClick={() => toggleLike(id)}>{heart}</button>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Perfect!

</section>
</replace-with-relevant-semantic-element>
</div>
);
};

ChatEntry.propTypes = {
// Fill with correct proptypes
id: PropTypes.number.isRequired,
sender: PropTypes.string.isRequired,
body: PropTypes.string.isRequired,
timeStamp: PropTypes.string.isRequired,
liked: PropTypes.bool.isRequired,
toggleLike: PropTypes.func.isRequired,
isLocal: PropTypes.bool,

Comment on lines +21 to +28
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Don't forget that in React PropTypes have been sunsetted and you will want to use Typescript in practice now!

};

export default ChatEntry;
2 changes: 1 addition & 1 deletion src/components/ChatLog.test.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ChatLog from './ChatLog';
import ChatLog from './Chatlog';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';

Expand Down
42 changes: 42 additions & 0 deletions src/components/Chatlog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import ChatEntry from './ChatEntry';
import './ChatLog.css';
import PropTypes from 'prop-types';

const ChatLog = ({entries, toggleLike = () => {}, local}) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@Bipentium25, interested to know why you chose to implement toggleLike a de-structured prop like this.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Oh I see now, is it to more readily show that toggleLike is a function? @Bipentium25

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I had a problem back then when I was uploading the chatlog for test on learn. And I had a error message "Warning: Failed prop type: The prop toggleLike is marked as required in ChatLog, but its value is undefined." and was suggested doing this by chatgpt" ⚠️ The warnings are from PropTypes checks in development mode. React is telling you that a prop marked as required (toggleLike or local) wasn’t passed, but it doesn’t make the test fail.
""Give default values for those props in the component:""const ChatEntry = ({ toggleLike = () => {}, ... }) => { ... }
const ChatLog = ({ toggleLike = () => {}, local = '' , ... }) => { ... }"

const chatlogFromData = entries.map(chatEntry => {
return(
<ul key={chatEntry.id}>
<ChatEntry
id = {chatEntry.id}
sender = {chatEntry.sender}
body = {chatEntry.body}
timeStamp = {chatEntry.timeStamp}
liked = {chatEntry.liked}
toggleLike={toggleLike}
Comment on lines +10 to +15
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Convention is have no whitespace surrounding your =. It is to model the formatting you see in HTML attributes.

Suggested change
id = {chatEntry.id}
sender = {chatEntry.sender}
body = {chatEntry.body}
timeStamp = {chatEntry.timeStamp}
liked = {chatEntry.liked}
toggleLike={toggleLike}
id={chatEntry.id}
sender={chatEntry.sender}
body={chatEntry.body}
timeStamp={chatEntry.timeStamp}
liked={chatEntry.liked}
toggleLike={toggleLike}

isLocal={chatEntry.sender === local}>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

👍🏿

</ChatEntry>
</ul>
);
});
return(
<ul className="chatlog">
{chatlogFromData}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

</ul>
);
};
ChatLog.propTypes = {
entries: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
sender: PropTypes.string.isRequired,
body: PropTypes.string.isRequired,
timeStamp: PropTypes.string.isRequired,
liked: PropTypes.bool.isRequired,
})
).isRequired,

toggleLike: PropTypes.func.isRequired,
local: PropTypes.string.isRequired,
};
Comment on lines +27 to +40
Copy link
Copy Markdown

Choose a reason for hiding this comment

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


export default ChatLog;