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
56 changes: 54 additions & 2 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,71 @@
import React from 'react';
import './App.css';
// import ChatEntry from './components/ChatEntry';
import chatMessages from './data/messages.json';
import ChatLog from './components/ChatLog';

const App = () => {
const [messages, setMessages] = React.useState([]);

Choose a reason for hiding this comment

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

If you add useState as an import, you wouldn't need to refer to the React object in order to use useState.

import React, { useState } from 'react';
  const [messages, setMessages] = useState([]);

//
React.useEffect(() => {
// populate message state with stored messages
setMessages(chatMessages);
}, []);

const heartToggling = (updatedMessage) => {
const updatedMessages = messages.map((message) => {
if (message.id === updatedMessage.id) {
return updatedMessage;
} else {
return message;
}
});

setMessages(updatedMessages);
};
Comment on lines +15 to +25

Choose a reason for hiding this comment

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

Nice work managing the data and returning a brand new object!

Here's a refresher on updating state in React:

React components re-render whenever there is a change/update to state data. In this case, our data is an object and we are modifying values nested within that object.

Under the hood, React uses Object.is() to compare the previous state object with the one that's been provided viasetMessages(updatedMessages). Object.is() checks if the object has changed and more specifically if the object passed has a different reference in memory. In Javascript, changing the properties and/or values in an object does NOT change the object reference, which is why we must create a new version of our state object ( contains a copy of all the properties that weren't updated along with the properties/values that were).

Here is an article with more info: https://www.valentinog.com/blog/react-object-is/


let heartCount = 0;
for (const eachMessage of messages) {
if (eachMessage.liked) {
heartCount += 1;
}
}

//optional:decide local and remote~~~~~~~~~~~~~~~~~~~~~~~~~
const local = chatMessages[0].sender;
let remote;
for (const eachMessage of chatMessages) {
if (eachMessage.sender !== local) {
remote = eachMessage.sender;
}
}
Comment on lines +35 to +41

Choose a reason for hiding this comment

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

Nice work differentiating the chat participants!

// console.log('App', local);

Choose a reason for hiding this comment

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

In industry, you'll be submitting PR's containing code you'd like your team members to have. In this case, is it necessary for your teammates to have this console.log ? If not, we can remove this line.

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

return (
<div id="App">
<header>
<h1>Application title</h1>
<h1>
{/* Chat between {chatMessages[0]['sender']} and{' '}
{chatMessages[1]['sender']} */}
Chat between {local} and {remote}
</h1>
<h2>{heartCount} ❤️s</h2>
</header>
<main>
{/* Wave 01: Render one ChatEntry component
Wave 02: Render ChatLog component */}
// Wave 02: Render ChatLog component */}
<ChatLog
entries={messages}
heartToggling={heartToggling}
local={local}
></ChatLog>
</main>
</div>
);
};

export default App;

//create count state in the app comp, pass down the updated message to the chatEntry comp,
//every time a heart is clicked, increase the count by 1
3 changes: 2 additions & 1 deletion src/components/ChatEntry.css
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,5 @@ button {

.chat-entry.remote .entry-bubble:hover::before {
background-color: #a9f6f6;
}
}

55 changes: 49 additions & 6 deletions src/components/ChatEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,63 @@ import './ChatEntry.css';
import PropTypes from 'prop-types';

const ChatEntry = (props) => {
//display tomes as ..years ago
let currentYear = new Date().getFullYear();
let pastYear = parseInt(props.timeStamp.slice(0, 4));
// console.log(pastYear);
const agoTime = currentYear - pastYear;
Comment on lines +7 to +10

Choose a reason for hiding this comment

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

Good work finding the year sent.

console.log(props.sender);

Choose a reason for hiding this comment

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

Be sure to remove all print statements/console logs in PRs. Anything that we do locally to test our code should be left out of PRs.


//event handler for like button click
const handleHeartClick = () => {
const messageUpdated = {
id: props.id,
sender: props.sender,
body: props.body,
timeStamp: props.timeStamp,
liked: !props.liked,
};
props.heartToggling(messageUpdated);
};

const button = props.liked ? (
<button onClick={handleHeartClick} className="like">
❤️
</button>
) : (
<button onClick={handleHeartClick} className="like">
🤍
</button>
);
Comment on lines +14 to +33

Choose a reason for hiding this comment

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

Great work constructing the event handler and using conditional rendering to change the heart icon!


//optional decide local and remote~~~~~~~~~~~~~~~~~~~~~
const chatEntry = props.sender === props.local ? 'local' : 'remote';
// console.log('Entry', props.chatEntry);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

return (
<div className="chat-entry local">
<h2 className="entry-name">Replace with name of sender</h2>
// <div className="chat-entry local">
<div className={`chat-entry ${chatEntry}`}>
<h2 className="entry-name">{props.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>
<p>{props.body}</p>
<p className="entry-time">{agoTime} years ago</p>
{/* <button onClick={handleHeartClick} className="like">
🤍
</button> */}
{button}
</section>
</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,
heartToggling: PropTypes.func.isRequired,
};
Comment on lines 56 to 63

Choose a reason for hiding this comment

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

👍


export default ChatEntry;
53 changes: 53 additions & 0 deletions src/components/ChatLog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';
import ChatEntry from './ChatEntry';
import './ChatLog.css';
import PropTypes from 'prop-types';

const ChatLog = (props) => {
const chatEntryComp = props.entries.map((chatMessage, index) => {
// const updateMessage = (updatedMessage) => {
// //update only the current message
// const allMessages = [...props.messages];
// allMessages[index] = updatedMessage;
// props.setMessages(allMessages);
// };

//optional: decide local and remote~~~~~~~~~~~~~
// console.log('Log', props.local);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Comment on lines +8 to +18

Choose a reason for hiding this comment

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

Please remove commented-out code in future PR's. This work could exist in a local branch.

return (
<li key={index}>
<ChatEntry
id={chatMessage.id}
sender={chatMessage.sender}
body={chatMessage.body}
timeStamp={chatMessage.timeStamp}
liked={chatMessage.liked}
heartToggling={props.heartToggling}
local={props.local}
></ChatEntry>
</li>
);
});
return (
<section>
<ul>{chatEntryComp}</ul>
</section>
);
};

ChatLog.propTypes = {
messages: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
sender: PropTypes.string.isRequired,
body: PropTypes.string.isRequired,
timeStamp: PropTypes.string.isRequired,
liked: PropTypes.bool,
})
),
heartToggling: PropTypes.func.isRequired,
};
Comment on lines +40 to +51

Choose a reason for hiding this comment

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

👍 Great work setting up propTypes


export default ChatLog;
48 changes: 24 additions & 24 deletions src/components/ChatLog.test.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,49 @@
import React from "react";
import "@testing-library/jest-dom/extend-expect";
import ChatLog from "./ChatLog";
import { render, screen } from "@testing-library/react";
import React from 'react';

Choose a reason for hiding this comment

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

Good catch on changing the single quotes! ESLinters are really finicky about style rules!

import '@testing-library/jest-dom/extend-expect';
import ChatLog from './ChatLog';
import { render, screen } from '@testing-library/react';

const LOG = [
{
sender: "Vladimir",
body: "why are you arguing with me",
timeStamp: "2018-05-29T22:49:06+00:00",
sender: 'Vladimir',
body: 'why are you arguing with me',
timeStamp: '2018-05-29T22:49:06+00:00',
},
{
sender: "Estragon",
body: "Because you are wrong.",
timeStamp: "2018-05-29T22:49:33+00:00",
sender: 'Estragon',
body: 'Because you are wrong.',
timeStamp: '2018-05-29T22:49:33+00:00',
},
{
sender: "Vladimir",
body: "because I am what",
timeStamp: "2018-05-29T22:50:22+00:00",
sender: 'Vladimir',
body: 'because I am what',
timeStamp: '2018-05-29T22:50:22+00:00',
},
{
sender: "Estragon",
body: "A robot.",
timeStamp: "2018-05-29T22:52:21+00:00",
sender: 'Estragon',
body: 'A robot.',
timeStamp: '2018-05-29T22:52:21+00:00',
},
{
sender: "Vladimir",
body: "Notabot",
timeStamp: "2019-07-23T22:52:21+00:00",
sender: 'Vladimir',
body: 'Notabot',
timeStamp: '2019-07-23T22:52:21+00:00',
},
];

describe("Wave 02: ChatLog", () => {
describe('Wave 02: ChatLog', () => {
beforeEach(() => {
render(<ChatLog entries={LOG} />);
});

test("renders without crashing and shows all the names", () => {
test('renders without crashing and shows all the names', () => {
[
{
name: "Vladimir",
name: 'Vladimir',
numChats: 3,
},
{
name: "Estragon",
name: 'Estragon',
numChats: 2,
},
].forEach((person) => {
Expand All @@ -56,7 +56,7 @@ describe("Wave 02: ChatLog", () => {
});
});

test("renders an empty list without crashing", () => {
test('renders an empty list without crashing', () => {
const element = render(<ChatLog entries={[]} />);
expect(element).not.toBeNull();
});
Expand Down