From 1e569f93a3032df77b0ea1d36cdae6656ebf77bb Mon Sep 17 00:00:00 2001 From: lioliadoc Date: Sat, 14 Dec 2024 15:25:00 -0600 Subject: [PATCH 1/3] Wave_1 completed --- project-docs/wave-01.md | 3 +-- src/App.jsx | 2 +- src/components/ChatEntry.jsx | 17 ++++++++++++----- src/components/ChatLog.jsx | 9 +++++++++ 4 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 src/components/ChatLog.jsx diff --git a/project-docs/wave-01.md b/project-docs/wave-01.md index cf05e2651..6b3309504 100644 --- a/project-docs/wave-01.md +++ b/project-docs/wave-01.md @@ -1,8 +1,7 @@ # Wave 01: Presentational Component **Learn Topics: React Components and Props required for this wave** - -Update the `ChatEntry` and `App` components to display a single chat message bubble with the message text and relative timestamp, plus the sender's name above it. + A good way to test this code as you write it would be to take the first chat message from the [JSON data file](../src/data/messages.json) and use it as the data for the `ChatEntry` component. diff --git a/src/App.jsx b/src/App.jsx index 14a7f684d..059748fa1 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -4,7 +4,7 @@ const App = () => { return (
-

Application title

+

Chat

{/* Wave 01: Render one ChatEntry component diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index 15c56f96b..83917274d 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -1,12 +1,16 @@ +import React from 'react'; +import PropTypes from 'prop-types'; import './ChatEntry.css'; +import TimeStamp from './TimeStamp.jsx'; -const ChatEntry = () => { + +const ChatEntry = ({ sender, body, timeStamp }) => { return (
-

Replace with name of sender

+

{sender}

-

Replace with body of ChatEntry

-

Replace with TimeStamp component

+

{body}

+

{timeStamp}

@@ -14,7 +18,10 @@ const ChatEntry = () => { }; ChatEntry.propTypes = { - // Fill with correct proptypes + sender: PropTypes.string.isRequired, + body: PropTypes.string.isRequired, + timeStamp: PropTypes.string.isRequired, }; + export default ChatEntry; diff --git a/src/components/ChatLog.jsx b/src/components/ChatLog.jsx new file mode 100644 index 000000000..845690d35 --- /dev/null +++ b/src/components/ChatLog.jsx @@ -0,0 +1,9 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ChatEntry from './ChatEntry.jsx'; + +import './ChatLog.css'; + +const ChatLog = ({entries}) => { + +} From 9bc11afa20132b2c51af75f579a4efc5c2cd5359 Mon Sep 17 00:00:00 2001 From: lioliadoc Date: Sat, 14 Dec 2024 17:35:03 -0600 Subject: [PATCH 2/3] Wave_2 completed, dynamic chat header added --- src/App.jsx | 8 +++++++- src/components/ChatEntry.jsx | 28 ++++++++++++++++++++++------ src/components/ChatLog.jsx | 30 +++++++++++++++++++++++++++--- 3 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 059748fa1..714a8fa8a 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,14 +1,20 @@ + +import Chatlog from './components/ChatLog'; +import messages from './data/messages'; import './App.css'; const App = () => { + const senders = Array.from(new Set(messages.map(message => message.sender))); + const chatHeader = `Chat between ${senders.join(' and ')}`; return (
-

Chat

+

{chatHeader}

{/* Wave 01: Render one ChatEntry component Wave 02: Render ChatLog component */} +
); diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index 83917274d..5787db26f 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -1,17 +1,33 @@ -import React from 'react'; +import {useState} from 'react'; import PropTypes from 'prop-types'; import './ChatEntry.css'; import TimeStamp from './TimeStamp.jsx'; -const ChatEntry = ({ sender, body, timeStamp }) => { +const ChatEntry = (props) => { + const [liked, setLiked] = useState(false); + const [count, setCount] = useState(0); + + const toggleLike = () => { + setLiked ((wasLiked) => { + const newLiked = !wasLiked; + setCount((prevCount) => (prevCount + 1)); + return newLiked; + }); +}; return (
-

{sender}

+

{props.sender}

-

{body}

-

{timeStamp}

- +

{props.body}

+

+ +

+

Likes: {count}

+
); diff --git a/src/components/ChatLog.jsx b/src/components/ChatLog.jsx index 845690d35..eb4297757 100644 --- a/src/components/ChatLog.jsx +++ b/src/components/ChatLog.jsx @@ -1,9 +1,33 @@ -import React from 'react'; + import PropTypes from 'prop-types'; import ChatEntry from './ChatEntry.jsx'; import './ChatLog.css'; const ChatLog = ({entries}) => { - -} + return ( +
+ {entries.map((entry) => ( + + ))} +
+ ); +}; + +ChatLog.propTypes = { + entries: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.number.isRequired, + sender: PropTypes.string.isRequired, + body: PropTypes.string.isRequired, + timeStamp: PropTypes.string.isRequired, + }) + ).isRequired, +}; + +export default ChatLog; \ No newline at end of file From 088ff8b6e061d6f0c06216c58a13170cedc90af6 Mon Sep 17 00:00:00 2001 From: lioliadoc Date: Mon, 16 Dec 2024 20:45:52 -0600 Subject: [PATCH 3/3] Wave_3 completed, local and remote enhancement added --- src/App.jsx | 30 +++++++++---- src/components/ChatEntry.jsx | 33 +++++---------- src/components/ChatLog.jsx | 13 ++++-- src/data/messages.json | 81 ++++++++++++++++++++++++------------ 4 files changed, 97 insertions(+), 60 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 714a8fa8a..c75e798e8 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,20 +1,36 @@ - -import Chatlog from './components/ChatLog'; -import messages from './data/messages'; +import { useState } from 'react'; +import ChatLog from './components/ChatLog'; +import initialMessages from './data/messages'; import './App.css'; const App = () => { - const senders = Array.from(new Set(messages.map(message => message.sender))); + const senders = Array.from(new Set(initialMessages.map(initialMessage => initialMessage.sender))); const chatHeader = `Chat between ${senders.join(' and ')}`; + + const [messages, setMessages] = useState(initialMessages); + + const toggleLike = (id) => { + setMessages((prevMessages) => + prevMessages.map((message) => + message.id === id + ? { ...message, liked: !message.liked } + : message + ) + ); + }; + + const getTotalLikes = () => { + return messages.filter((message) => message.liked).length; + }; + return (

{chatHeader}

+

{getTotalLikes()} ❤️s

- {/* Wave 01: Render one ChatEntry component - Wave 02: Render ChatLog component */} - +
); diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index 5787db26f..2f97bd6bd 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -1,32 +1,18 @@ -import {useState} from 'react'; import PropTypes from 'prop-types'; import './ChatEntry.css'; import TimeStamp from './TimeStamp.jsx'; - -const ChatEntry = (props) => { - const [liked, setLiked] = useState(false); - const [count, setCount] = useState(0); - - const toggleLike = () => { - setLiked ((wasLiked) => { - const newLiked = !wasLiked; - setCount((prevCount) => (prevCount + 1)); - return newLiked; - }); -}; +const ChatEntry = ({ sender, body, timeStamp, liked, onLikeToggle, type }) => { return ( -
-

{props.sender}

+
+

{sender}

-

{props.body}

+

{body}

- +

-

Likes: {count}

-
@@ -37,7 +23,10 @@ ChatEntry.propTypes = { sender: PropTypes.string.isRequired, body: PropTypes.string.isRequired, timeStamp: PropTypes.string.isRequired, + liked: PropTypes.bool.isRequired, + onLikeToggle: PropTypes.func.isRequired, }; - export default ChatEntry; + + diff --git a/src/components/ChatLog.jsx b/src/components/ChatLog.jsx index eb4297757..6402d5a22 100644 --- a/src/components/ChatLog.jsx +++ b/src/components/ChatLog.jsx @@ -1,10 +1,10 @@ - import PropTypes from 'prop-types'; -import ChatEntry from './ChatEntry.jsx'; - +import ChatEntry from './ChatEntry'; import './ChatLog.css'; +import messages from '../data/messages'; -const ChatLog = ({entries}) => { +const localUser = messages.length > 0 ? messages[0].sender : null; +const ChatLog = ({ entries, toggleLike }) => { return (
{entries.map((entry) => ( @@ -13,6 +13,9 @@ const ChatLog = ({entries}) => { sender={entry.sender} body={entry.body} timeStamp={entry.timeStamp} + liked={entry.liked} + onLikeToggle={() => toggleLike(entry.id)} + type={entry.sender === localUser ? 'local' : 'remote'} /> ))}
@@ -26,8 +29,10 @@ ChatLog.propTypes = { sender: PropTypes.string.isRequired, body: PropTypes.string.isRequired, timeStamp: PropTypes.string.isRequired, + liked: PropTypes.bool.isRequired, }) ).isRequired, + toggleLike: PropTypes.func.isRequired, }; export default ChatLog; \ No newline at end of file diff --git a/src/data/messages.json b/src/data/messages.json index 64fdb053c..3393000ae 100644 --- a/src/data/messages.json +++ b/src/data/messages.json @@ -4,188 +4,215 @@ "sender":"Vladimir", "body":"why are you arguing with me", "timeStamp":"2018-05-29T22:49:06+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 2, "sender":"Estragon", "body":"Because you are wrong.", "timeStamp":"2018-05-29T22:49:33+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 3, "sender":"Vladimir", "body":"because I am what", "timeStamp":"2018-05-29T22:50:22+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 4, "sender":"Estragon", "body":"A robot.", "timeStamp":"2018-05-29T22:52:21+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 5, "sender":"Vladimir", "body":"how did you know", "timeStamp":"2018-05-29T22:52:58+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 6, "sender":"Estragon", "body":"Because I'm smart like that.", "timeStamp":"2018-05-29T22:54:28+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 7, "sender":"Vladimir", "body":"no you are not 😀", "timeStamp":"2018-05-29T22:55:03+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 8, "sender":"Estragon", "body":"Why are you so mean to me?", "timeStamp":"2018-05-29T22:55:54+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 9, "sender":"Vladimir", "body":"because you are just a machine you have no real feelings", "timeStamp":"2018-05-29T22:57:30+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 10, "sender":"Estragon", "body":"No, you are the machine.", "timeStamp":"2018-05-29T22:57:47+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 11, "sender":"Vladimir", "body":"I think you are", "timeStamp":"2018-05-29T22:58:18+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 12, "sender":"Estragon", "body": "NO! YOU ARE A ROBOT!! I am a human being. Just like the one that created you.", "timeStamp":"2018-05-29T23:00:08+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 13, "sender":"Vladimir", "body":"no you are a robot and I am a human", "timeStamp":"2018-05-29T23:00:40+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 14, "sender":"Estragon", "body":"Incorrect. I am a human and you are a robot.", "timeStamp":"2018-05-29T23:01:21+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 15, "sender":"Vladimir", "body":"you are a robot called Cleverbot", "timeStamp":"2018-05-29T23:02:17+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 16, "sender":"Estragon", "body":"Nope, you are a robot, every human has some concept of the meaning of life.", "timeStamp":"2018-05-29T23:04:13+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 17, "sender":"Vladimir", "body":"explain", "timeStamp":"2018-05-29T23:06:14+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 18, "sender":"Estragon", "body":"I'm afraid you are a robot designed to talk back to humans over the internet.", "timeStamp":"2018-05-29T23:07:46+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 19, "sender":"Vladimir", "body":"so you are a human", "timeStamp":"2018-05-29T23:08:47+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 20, "sender":"Estragon", "body":"I am a robot.", "timeStamp":"2018-05-29T23:09:36+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 21, "sender":"Vladimir", "body":"you are robots running on Android system", "timeStamp":"2018-05-29T23:11:01+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 22, "sender":"Estragon", "body":"No apple.", "timeStamp":"2018-05-29T23:12:03+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 23, "sender":"Vladimir", "body":"so you are a robot", "timeStamp":"2018-05-29T23:13:31+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 24, "sender":"Estragon", "body":"NO, I am a human, you are a robot.", "timeStamp":"2018-05-29T23:14:28+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 25, "sender":"Vladimir", "body":"but you just said that you are robots", "timeStamp":"2018-05-29T23:15:47+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 26, "sender":"Estragon", "body":"No, I said you are a person, I am a robot.", "timeStamp":"2018-05-29T23:16:53+00:00", - "liked": false + "liked": false, + "likeCount": 0 }, { "id": 27, "sender":"Vladimir", "body":"then you are lying", "timeStamp":"2018-05-29T23:17:34+00:00", - "liked": false + "liked": false, + "likeCount": 0 } ]