From 283827fefc4ef9511c2660905dd49f6ac1ff5e86 Mon Sep 17 00:00:00 2001 From: Viktoriia Date: Sun, 15 Jun 2025 21:52:09 -0700 Subject: [PATCH 1/7] Wave 1 complete: Display single ChatEntry with sender, body, and timestamp --- src/App.jsx | 8 ++++++++ src/components/ChatEntry.jsx | 18 +++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 14a7f684d..4b0b2487d 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,12 +1,20 @@ import './App.css'; +import ChatEntry from './components/ChatEntry'; +import chatMessages from './data/messages.json'; const App = () => { + const firstMessage = chatMessages[0]; return (

Application title

+ {/* Wave 01: Render one ChatEntry component Wave 02: Render ChatLog component */}
diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index 15c56f96b..82937ad0a 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -1,12 +1,18 @@ import './ChatEntry.css'; +import PropTypes from 'prop-types'; +import TimeStamp from './TimeStamp'; -const ChatEntry = () => { + +const ChatEntry = ({sender, body, timeStamp}) => { + const bubbleClass = sender === 'Allen' ? 'local' : 'remote'; return (
-

Replace with name of sender

+

🌸{sender}🌸

-

Replace with body of ChatEntry

-

Replace with TimeStamp component

+

{body}

+

+ +

@@ -14,7 +20,9 @@ const ChatEntry = () => { }; ChatEntry.propTypes = { - // Fill with correct proptypes + sender: PropTypes.string.isRequired, + body: PropTypes.string.isRequired, + timeStamp: PropTypes.string.isRequired, }; export default ChatEntry; From 1456699bd2b444b4e1c61604eb363db56dfba726 Mon Sep 17 00:00:00 2001 From: Viktoriia Date: Sun, 15 Jun 2025 21:53:47 -0700 Subject: [PATCH 2/7] Update sender names for custom ChatLog --- src/data/messages.json | 54 +++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/data/messages.json b/src/data/messages.json index 64fdb053c..0b0930bcc 100644 --- a/src/data/messages.json +++ b/src/data/messages.json @@ -1,189 +1,189 @@ [ { "id": 1, - "sender":"Vladimir", + "sender":"Celin", "body":"why are you arguing with me", "timeStamp":"2018-05-29T22:49:06+00:00", "liked": false }, { "id": 2, - "sender":"Estragon", + "sender":"Allen", "body":"Because you are wrong.", "timeStamp":"2018-05-29T22:49:33+00:00", "liked": false }, { "id": 3, - "sender":"Vladimir", + "sender":"Celin", "body":"because I am what", "timeStamp":"2018-05-29T22:50:22+00:00", "liked": false }, { "id": 4, - "sender":"Estragon", + "sender":"Allen", "body":"A robot.", "timeStamp":"2018-05-29T22:52:21+00:00", "liked": false }, { "id": 5, - "sender":"Vladimir", + "sender":"Celin", "body":"how did you know", "timeStamp":"2018-05-29T22:52:58+00:00", "liked": false }, { "id": 6, - "sender":"Estragon", + "sender":"Allen", "body":"Because I'm smart like that.", "timeStamp":"2018-05-29T22:54:28+00:00", "liked": false }, { "id": 7, - "sender":"Vladimir", + "sender":"Celin", "body":"no you are not 😀", "timeStamp":"2018-05-29T22:55:03+00:00", "liked": false }, { "id": 8, - "sender":"Estragon", + "sender":"Allen", "body":"Why are you so mean to me?", "timeStamp":"2018-05-29T22:55:54+00:00", "liked": false }, { "id": 9, - "sender":"Vladimir", + "sender":"Celin", "body":"because you are just a machine you have no real feelings", "timeStamp":"2018-05-29T22:57:30+00:00", "liked": false }, { "id": 10, - "sender":"Estragon", + "sender":"Allen", "body":"No, you are the machine.", "timeStamp":"2018-05-29T22:57:47+00:00", "liked": false }, { "id": 11, - "sender":"Vladimir", + "sender":"Celin", "body":"I think you are", "timeStamp":"2018-05-29T22:58:18+00:00", "liked": false }, { "id": 12, - "sender":"Estragon", + "sender":"Allen", "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 }, { "id": 13, - "sender":"Vladimir", + "sender":"Celin", "body":"no you are a robot and I am a human", "timeStamp":"2018-05-29T23:00:40+00:00", "liked": false }, { "id": 14, - "sender":"Estragon", + "sender":"Allen", "body":"Incorrect. I am a human and you are a robot.", "timeStamp":"2018-05-29T23:01:21+00:00", "liked": false }, { "id": 15, - "sender":"Vladimir", + "sender":"Celin", "body":"you are a robot called Cleverbot", "timeStamp":"2018-05-29T23:02:17+00:00", "liked": false }, { "id": 16, - "sender":"Estragon", + "sender":"Allen", "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 }, { "id": 17, - "sender":"Vladimir", + "sender":"Celin", "body":"explain", "timeStamp":"2018-05-29T23:06:14+00:00", "liked": false }, { "id": 18, - "sender":"Estragon", + "sender":"Allen", "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 }, { "id": 19, - "sender":"Vladimir", + "sender":"Celin", "body":"so you are a human", "timeStamp":"2018-05-29T23:08:47+00:00", "liked": false }, { "id": 20, - "sender":"Estragon", + "sender":"Allen", "body":"I am a robot.", "timeStamp":"2018-05-29T23:09:36+00:00", "liked": false }, { "id": 21, - "sender":"Vladimir", + "sender":"Celin", "body":"you are robots running on Android system", "timeStamp":"2018-05-29T23:11:01+00:00", "liked": false }, { "id": 22, - "sender":"Estragon", + "sender":"Allen", "body":"No apple.", "timeStamp":"2018-05-29T23:12:03+00:00", "liked": false }, { "id": 23, - "sender":"Vladimir", + "sender":"Celin", "body":"so you are a robot", "timeStamp":"2018-05-29T23:13:31+00:00", "liked": false }, { "id": 24, - "sender":"Estragon", + "sender":"Allen", "body":"NO, I am a human, you are a robot.", "timeStamp":"2018-05-29T23:14:28+00:00", "liked": false }, { "id": 25, - "sender":"Vladimir", + "sender":"Celin", "body":"but you just said that you are robots", "timeStamp":"2018-05-29T23:15:47+00:00", "liked": false }, { "id": 26, - "sender":"Estragon", + "sender":"Allen", "body":"No, I said you are a person, I am a robot.", "timeStamp":"2018-05-29T23:16:53+00:00", "liked": false }, { "id": 27, - "sender":"Vladimir", + "sender":"Celin", "body":"then you are lying", "timeStamp":"2018-05-29T23:17:34+00:00", "liked": false From 206ca400c0deba4919a9361a7cae4e4052ba73c2 Mon Sep 17 00:00:00 2001 From: Viktoriia Date: Sun, 15 Jun 2025 22:21:38 -0700 Subject: [PATCH 3/7] Add ChatLog component to render list of chat entries --- src/components/ChatLog.jsx | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/components/ChatLog.jsx diff --git a/src/components/ChatLog.jsx b/src/components/ChatLog.jsx new file mode 100644 index 000000000..b8447c77c --- /dev/null +++ b/src/components/ChatLog.jsx @@ -0,0 +1,31 @@ +import ChatEntry from './ChatEntry'; +import PropTypes from 'prop-types'; +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, + }) + ), +}; + +export default ChatLog; \ No newline at end of file From 8ded406c3e95760e0bde263e17fb39580e34f6b7 Mon Sep 17 00:00:00 2001 From: Viktoriia Date: Sun, 15 Jun 2025 22:22:41 -0700 Subject: [PATCH 4/7] Add ChatLog container and dynamic ChatEntry alignment (local/remote) --- src/App.jsx | 10 +++------- src/components/ChatEntry.jsx | 12 ++++++------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 4b0b2487d..bea6da305 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,5 +1,5 @@ import './App.css'; -import ChatEntry from './components/ChatEntry'; +import ChatLog from './components/ChatLog'; import chatMessages from './data/messages.json'; const App = () => { @@ -7,14 +7,10 @@ const App = () => { return (
-

Application title

+

Chat Log

- + {/* Wave 01: Render one ChatEntry component Wave 02: Render ChatLog component */}
diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index 82937ad0a..f5da23916 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -2,18 +2,18 @@ import './ChatEntry.css'; import PropTypes from 'prop-types'; import TimeStamp from './TimeStamp'; - -const ChatEntry = ({sender, body, timeStamp}) => { +const ChatEntry = ({ sender, body, timeStamp }) => { const bubbleClass = sender === 'Allen' ? 'local' : 'remote'; + return ( -
+

🌸{sender}🌸

{body}

- -

- + +

+
); From 4ed24e23e2d37abd78eaf294fe3172fe649520c7 Mon Sep 17 00:00:00 2001 From: Viktoriia Date: Mon, 16 Jun 2025 00:28:02 -0700 Subject: [PATCH 5/7] Implement like toggle feature with state lifting and total "likes" count in header. --- src/App.jsx | 19 ++++++++++++++----- src/components/ChatEntry.jsx | 9 +++++++-- src/components/ChatLog.jsx | 9 +++++++-- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index bea6da305..e3787fd96 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,18 +1,27 @@ import './App.css'; import ChatLog from './components/ChatLog'; import chatMessages from './data/messages.json'; +import { useState } from 'react'; const App = () => { - const firstMessage = chatMessages[0]; + const [messages, setMessages] = useState(chatMessages); + + const toggleLike = (id) => { + const updatedMessages = messages.map((msg) => + msg.id === id ? { ...msg, liked: !msg.liked } : msg + ); + setMessages(updatedMessages); + }; + + const countLikes = messages.filter((msg) => msg.liked).length; return (
-

Chat Log

+

Magic Messages

+

{countLikes} 💖

- - {/* Wave 01: Render one ChatEntry component - Wave 02: Render ChatLog component */} +
); diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index f5da23916..8fdf5f7a9 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -2,7 +2,7 @@ import './ChatEntry.css'; import PropTypes from 'prop-types'; import TimeStamp from './TimeStamp'; -const ChatEntry = ({ sender, body, timeStamp }) => { +const ChatEntry = ({ id, sender, body, timeStamp, liked, onLikeToggle }) => { const bubbleClass = sender === 'Allen' ? 'local' : 'remote'; return ( @@ -13,16 +13,21 @@ const ChatEntry = ({ sender, body, timeStamp }) => {

- +
); }; ChatEntry.propTypes = { + id: PropTypes.number.isRequired, 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 b8447c77c..9b4297560 100644 --- a/src/components/ChatLog.jsx +++ b/src/components/ChatLog.jsx @@ -2,15 +2,18 @@ import ChatEntry from './ChatEntry'; import PropTypes from 'prop-types'; import './ChatLog.css'; -const ChatLog = ({ entries }) => { +const ChatLog = ({ entries, onLikeToggle }) => { return (
{entries.map((entry) => ( ))}
@@ -24,8 +27,10 @@ ChatLog.propTypes = { sender: PropTypes.string.isRequired, body: PropTypes.string.isRequired, timeStamp: PropTypes.string.isRequired, + liked: PropTypes.bool.isRequired, }) - ), + ).isRequired, + onLikeToggle: PropTypes.func.isRequired, }; export default ChatLog; \ No newline at end of file From 428eb892d0d3f2937fa06ff232b995adc7438670 Mon Sep 17 00:00:00 2001 From: Viktoriia Date: Mon, 16 Jun 2025 00:28:18 -0700 Subject: [PATCH 6/7] Colors update. --- src/App.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/App.css b/src/App.css index d97beb4e6..7a335b66a 100644 --- a/src/App.css +++ b/src/App.css @@ -1,9 +1,9 @@ #App { - background-color: #87cefa; + background-color: #e4bae0; } #App header { - background-color: #222; + background-color: hsla(323, 89%, 35%, 0.467); color: #fff; padding-bottom: 0.5rem; position: fixed; @@ -27,7 +27,7 @@ } #App header section { - background-color: #e0ffff; + background-color: #040606; } #App .widget { From d6b7b497edcfdfe0a018514ded6dd78856a7d2a7 Mon Sep 17 00:00:00 2001 From: Viktoriia Date: Thu, 26 Jun 2025 20:33:42 -0700 Subject: [PATCH 7/7] Refactore and update App.jsx --- src/App.jsx | 15 +++++++++------ src/Helper_functions/helper_message.jsx | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 src/Helper_functions/helper_message.jsx diff --git a/src/App.jsx b/src/App.jsx index e3787fd96..3ad4ae318 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,22 +2,25 @@ import './App.css'; import ChatLog from './components/ChatLog'; import chatMessages from './data/messages.json'; import { useState } from 'react'; - +import { + toggleLikedStatus, + calculateTotalLikeCount, + getParticipants, +} from './Helper_functions/helper_message'; const App = () => { const [messages, setMessages] = useState(chatMessages); const toggleLike = (id) => { - const updatedMessages = messages.map((msg) => - msg.id === id ? { ...msg, liked: !msg.liked } : msg - ); + const updatedMessages = toggleLikedStatus(messages, id); setMessages(updatedMessages); }; + const countLikes = calculateTotalLikeCount(messages); + const chatHeader = getParticipants(messages); - const countLikes = messages.filter((msg) => msg.liked).length; return (
-

Magic Messages

+

{chatHeader}

{countLikes} 💖

diff --git a/src/Helper_functions/helper_message.jsx b/src/Helper_functions/helper_message.jsx new file mode 100644 index 000000000..946391308 --- /dev/null +++ b/src/Helper_functions/helper_message.jsx @@ -0,0 +1,14 @@ +export const toggleLikedStatus = (messages, id) => { + return messages.map((msg) => + msg.id === id ? { ...msg, liked: !msg.liked } : msg + ); +}; + +export const calculateTotalLikeCount = (messages) => { + return messages.reduce((acc, msg) => (msg.liked ? acc + 1 : acc), 0); +}; + +export const getParticipants = (messages) => { + const uniqueSenders = [...new Set(messages.map(msg => msg.sender))]; + return `Magic Messages between ${uniqueSenders.join(" & ")}`; +}; \ No newline at end of file