From 12bc3125b2ad4329a0f42a89a2b7a41bdc5a66e2 Mon Sep 17 00:00:00 2001 From: Nikki Vaughan Date: Tue, 10 Dec 2024 11:02:21 -0500 Subject: [PATCH 1/5] Complete wave 1: Render single ChatEntry and tests pass --- src/App.jsx | 8 ++++++++ src/components/ChatEntry.jsx | 20 +++++++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 14a7f684..351f1f57 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,4 +1,6 @@ import './App.css'; +import ChatEntry from './components/ChatEntry'; +import messages from './data/messages.json'; const App = () => { return ( @@ -9,6 +11,12 @@ const App = () => {
{/* Wave 01: Render one ChatEntry component Wave 02: Render ChatLog component */} +
); diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index 15c56f96..427bd162 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -1,20 +1,26 @@ import './ChatEntry.css'; +import TimeStamp from './TimeStamp'; +import PropTypes from 'prop-types'; -const ChatEntry = () => { +const ChatEntry = (props) => { return (
-

Replace with name of sender

-
-

Replace with body of ChatEntry

-

Replace with TimeStamp component

- +
+

{props.sender}

+
+

{props.body}

+

+ +
); }; ChatEntry.propTypes = { - // Fill with correct proptypes + sender: PropTypes.string.isRequired, + body: PropTypes.string.isRequired, + timeStamp: PropTypes.string.isRequired, }; export default ChatEntry; From 88b71e12ba1dcb4bb42aa2f8a88729fb8c9ddb1a Mon Sep 17 00:00:00 2001 From: Nikki Vaughan Date: Tue, 10 Dec 2024 11:40:54 -0500 Subject: [PATCH 2/5] Complete wave 2: Add ChatLog component and iterated through all messages. Add optional enhancements for title, and local vs remote messages. --- src/App.jsx | 20 ++++++++++---------- src/components/ChatEntry.jsx | 5 ++++- src/components/ChatLog.jsx | 31 +++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 src/components/ChatLog.jsx diff --git a/src/App.jsx b/src/App.jsx index 351f1f57..fffea14c 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,22 +1,22 @@ import './App.css'; -import ChatEntry from './components/ChatEntry'; +import ChatLog from './components/ChatLog'; import messages from './data/messages.json'; const App = () => { + const participantList = []; + messages.forEach((message) => { + if (!participantList.includes(message.sender)) { + participantList.push(message.sender); + } + }); + return (
-

Application title

+

Chat between {participantList[0]} and {participantList[1]}

- {/* Wave 01: Render one ChatEntry component - Wave 02: Render ChatLog component */} - +
); diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index 427bd162..8a78ef42 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -3,8 +3,11 @@ import TimeStamp from './TimeStamp'; import PropTypes from 'prop-types'; const ChatEntry = (props) => { + const senderLocation = props.sender === 'Estragon' + ? 'chat-entry remote' + : 'chat-entry local'; return ( -
+

{props.sender}

diff --git a/src/components/ChatLog.jsx b/src/components/ChatLog.jsx new file mode 100644 index 00000000..4f1cc796 --- /dev/null +++ b/src/components/ChatLog.jsx @@ -0,0 +1,31 @@ +import './ChatLog.css'; +import PropTypes from 'prop-types'; +import ChatEntry from './ChatEntry'; + +function ChatLog(props) { + const chatMessages = props.entries.map((message) => { + return ( +
+ +
+ ); + }); + + return chatMessages; +} + +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; From 8d9ab759201001d3a0d2204ab6ad6a0960e32d8b Mon Sep 17 00:00:00 2001 From: Nikki Vaughan Date: Thu, 12 Dec 2024 10:56:18 -0500 Subject: [PATCH 3/5] Complete wave 3: Lifting state up to count liked messages --- src/App.jsx | 24 ++++++++++++++++++++++-- src/components/ChatEntry.jsx | 11 ++++++++++- src/components/ChatLog.jsx | 5 +++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index fffea14c..b46d0368 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,8 +1,25 @@ +import { useState } from 'react'; import './App.css'; import ChatLog from './components/ChatLog'; -import messages from './data/messages.json'; +import messageData from './data/messages.json'; const App = () => { + const [messages, setMessages] = useState(messageData); + const [likedCount, setLikedCount] = useState(0); + + const toggleLikedMessage = (messageId) => { + const updatedMessages = messages.map((message) => { + return message.id === messageId + ? { ...message, liked: !message.liked } + : message; + }); + + const likedList = updatedMessages.filter((message) => message.liked); + + setLikedCount(likedList.length); + setMessages(updatedMessages); + }; + const participantList = []; messages.forEach((message) => { if (!participantList.includes(message.sender)) { @@ -14,9 +31,12 @@ const App = () => {

Chat between {participantList[0]} and {participantList[1]}

+
+

{likedCount} ❤️s

+
- +
); diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index 8a78ef42..c2eb63f1 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -6,6 +6,7 @@ const ChatEntry = (props) => { const senderLocation = props.sender === 'Estragon' ? 'chat-entry remote' : 'chat-entry local'; + return (
@@ -13,7 +14,12 @@ const ChatEntry = (props) => {

{props.body}

- +
@@ -21,9 +27,12 @@ const ChatEntry = (props) => { }; ChatEntry.propTypes = { + id: PropTypes.number.isRequired, sender: PropTypes.string.isRequired, body: PropTypes.string.isRequired, timeStamp: PropTypes.string.isRequired, + liked: PropTypes.string.isRequired, + onMessageLiked: PropTypes.func.isRequired, }; export default ChatEntry; diff --git a/src/components/ChatLog.jsx b/src/components/ChatLog.jsx index 4f1cc796..1bf7074e 100644 --- a/src/components/ChatLog.jsx +++ b/src/components/ChatLog.jsx @@ -7,9 +7,12 @@ function ChatLog(props) { return (
); @@ -25,7 +28,9 @@ ChatLog.propTypes = { sender: PropTypes.string.isRequired, body: PropTypes.string.isRequired, timeStamp: PropTypes.string.isRequired, + liked: PropTypes.bool.isRequired, })).isRequired, + onMessageLiked: PropTypes.func.isRequired, }; export default ChatLog; From e176c1abb4490831724fb396bce4220603e587ab Mon Sep 17 00:00:00 2001 From: Nikki Vaughan Date: Thu, 12 Dec 2024 12:01:27 -0500 Subject: [PATCH 4/5] Add optional enhancement for user color choice --- src/App.jsx | 48 ++++++++++++++++++++++++++++++++++-- src/components/ChatEntry.jsx | 10 ++++++-- src/components/ChatLog.jsx | 4 +++ 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index b46d0368..27389b76 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -6,6 +6,8 @@ import messageData from './data/messages.json'; const App = () => { const [messages, setMessages] = useState(messageData); const [likedCount, setLikedCount] = useState(0); + const [remoteColor, setRemoteColor] = useState(); + const [localColor, setLocalColor] = useState(); const toggleLikedMessage = (messageId) => { const updatedMessages = messages.map((message) => { @@ -27,16 +29,58 @@ const App = () => { } }); + const colorBtns = (senderLocation) => { + const colors = [ + { '🔴': 'red' }, + { '🟠': 'orange' }, + { '🟡': 'yellow' }, + { '🟢': 'green' }, + { '🔵': 'blue' }, + { '🟣': 'purple' } + ]; + return colors.map((color, index) => { + return Object.entries(color).map(([colorIcon, colorName]) => { + return ( + + ); + }); + }); + }; + return (
-

Chat between {participantList[0]} and {participantList[1]}

+ +

Chat between

{' '} +

{participantList[0]}

{' '} +

and

{' '} +

{participantList[1]}

+
+ +

{participantList[0]}'s color:

+ {colorBtns('local')} +

{likedCount} ❤️s

+ +

{participantList[1]}'s color:

+ {colorBtns('remote')} +
- +
); diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index c2eb63f1..4775c9c3 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -3,16 +3,20 @@ import TimeStamp from './TimeStamp'; import PropTypes from 'prop-types'; const ChatEntry = (props) => { - const senderLocation = props.sender === 'Estragon' + const isRemote = props.sender === 'Estragon' ? true : false; + const senderLocation = isRemote ? 'chat-entry remote' : 'chat-entry local'; + const senderColor = isRemote + ? props.remoteColor + : props.localColor; return (

{props.sender}

-

{props.body}

+

{props.body}

); @@ -31,6 +33,8 @@ ChatLog.propTypes = { liked: PropTypes.bool.isRequired, })).isRequired, onMessageLiked: PropTypes.func.isRequired, + localColor: PropTypes.string.isRequired, + remoteColor: PropTypes.string.isRequired, }; export default ChatLog; From 127b56d7daa07ffa8c96f39493153ffdc21ecb1e Mon Sep 17 00:00:00 2001 From: Nikki Vaughan Date: Thu, 12 Dec 2024 12:15:08 -0500 Subject: [PATCH 5/5] Address warnings about undefined prop when it's marked as required --- src/components/ChatEntry.jsx | 6 +++--- src/components/ChatLog.jsx | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index 4775c9c3..7f7c996e 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -35,10 +35,10 @@ ChatEntry.propTypes = { sender: PropTypes.string.isRequired, body: PropTypes.string.isRequired, timeStamp: PropTypes.string.isRequired, - liked: PropTypes.string.isRequired, + liked: PropTypes.bool.isRequired, onMessageLiked: PropTypes.func.isRequired, - localColor: PropTypes.string.isRequired, - remoteColor: PropTypes.string.isRequired, + localColor: PropTypes.string, + remoteColor: PropTypes.string, }; export default ChatEntry; diff --git a/src/components/ChatLog.jsx b/src/components/ChatLog.jsx index e3d10470..31882e63 100644 --- a/src/components/ChatLog.jsx +++ b/src/components/ChatLog.jsx @@ -33,8 +33,8 @@ ChatLog.propTypes = { liked: PropTypes.bool.isRequired, })).isRequired, onMessageLiked: PropTypes.func.isRequired, - localColor: PropTypes.string.isRequired, - remoteColor: PropTypes.string.isRequired, + localColor: PropTypes.string, + remoteColor: PropTypes.string, }; export default ChatLog;