-
Notifications
You must be signed in to change notification settings - Fork 111
Shark - Ying Ye #101
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
base: main
Are you sure you want to change the base?
Shark - Ying Ye #101
Changes from all commits
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,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([]); | ||
| // | ||
| 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
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. 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 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
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. Nice work differentiating the chat participants! |
||
| // console.log('App', local); | ||
|
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. 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 |
||
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
|
||
| 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 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -97,4 +97,5 @@ button { | |
|
|
||
| .chat-entry.remote .entry-bubble:hover::before { | ||
| background-color: #a9f6f6; | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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
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. Good work finding the year sent. |
||
| console.log(props.sender); | ||
|
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. 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
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. 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
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. 👍 |
||
|
|
||
| export default ChatEntry; | ||
| 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
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. 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
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. 👍 Great work setting up propTypes |
||
|
|
||
| export default ChatLog; | ||
| 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'; | ||
|
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. 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) => { | ||
|
|
@@ -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(); | ||
| }); | ||
|
|
||
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.
If you add
useStateas an import, you wouldn't need to refer to theReactobject in order to useuseState.