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
78 changes: 75 additions & 3 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,86 @@
import { useState } from 'react';
import './App.css';
import ChatLog from './components/ChatLog';
import messageData from './data/messages.json';

const App = () => {
const [messages, setMessages] = useState(messageData);
const [likedCount, setLikedCount] = useState(0);
Copy link
Author

Choose a reason for hiding this comment

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

Do we necessarily need this piece of state? Couldn't we just use the fact that if a message is liked then we could have a function that calculates the total number of likes by iterating over the messages that is chatData? This works each render since each time a message is liked chatData state is changed.

const [remoteColor, setRemoteColor] = useState();
const [localColor, setLocalColor] = useState();

const toggleLikedMessage = (messageId) => {
const updatedMessages = messages.map((message) => {
return message.id === messageId
? { ...message, liked: !message.liked }
: message;
});
Comment on lines +12 to +17
Copy link
Author

Choose a reason for hiding this comment

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

Nice work on this function! By passing this down to your individual chat messages you are now able to have a single source of truth!


const likedList = updatedMessages.filter((message) => message.liked);

setLikedCount(likedList.length);
Copy link
Author

Choose a reason for hiding this comment

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

Like mentioned previously, we could ditch the extra piece of state and do the following:

const totalLikes = chatMessages.reduce((count, message) => count + (message.liked ? 1 : 0), 0);

Then we could use it like so:

<p className='widget' id='heartWidget'>{totalLikes} ❤️s</p>

setMessages(updatedMessages);
};

const participantList = [];
messages.forEach((message) => {
if (!participantList.includes(message.sender)) {
participantList.push(message.sender);
}
});

Comment on lines +25 to +31
Copy link
Author

Choose a reason for hiding this comment

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

Nice this would handle multiple (2+) participants!

const colorBtns = (senderLocation) => {
const colors = [
{ '🔴': 'red' },
{ '🟠': 'orange' },
{ '🟡': 'yellow' },
{ '🟢': 'green' },
{ '🔵': 'blue' },
{ '🟣': 'purple' }
];
return colors.map((color, index) => {
return Object.entries(color).map(([colorIcon, colorName]) => {
return (
<button
key={index}
onClick={() => {
senderLocation === 'local' ? setLocalColor(colorName) : setRemoteColor(colorName);
}}
className='widget'>{colorIcon}
</button>
);
});
});
};
Comment on lines +32 to +54
Copy link
Author

Choose a reason for hiding this comment

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

This function could be refactored, I don't think you need some of the logic that you have in here (specifically line 42). I would suggest coming back to this and seeing which parts could be changed so it could be more readable and a little more concise.


return (
<div id="App">
<header>
<h1>Application title</h1>
<span>
<h1>Chat between </h1>{' '}
<h1 className={localColor}>{participantList[0]}</h1>{' '}
<h1>and</h1>{' '}
<h1 className={remoteColor}>{participantList[1]}</h1>
Comment on lines +60 to +63
Copy link
Author

Choose a reason for hiding this comment

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

Perhaps we could combine some of the tags here to make it more readable. Especially since we are using a lot of the same tags.

</span>
<section>
<span className='widget'>
<h3 className={localColor}>{participantList[0]}&apos;s color:</h3>
{colorBtns('local')}
</span>
<p className='widget' id='heartWidget'>{likedCount} ❤️s</p>
<span className='widget'>
<h3 className={remoteColor}>{participantList[1]}&apos;s color:</h3>
{colorBtns('remote')}
</span>
</section>
</header>
<main>
{/* Wave 01: Render one ChatEntry component
Wave 02: Render ChatLog component */}
<ChatLog
entries={messages}
onMessageLiked={toggleLikedMessage}
localColor={localColor}
remoteColor={remoteColor}
/>
Comment on lines +78 to +83
Copy link
Author

Choose a reason for hiding this comment

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

⭐️

</main>
</div>
);
Expand Down
40 changes: 32 additions & 8 deletions src/components/ChatEntry.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
import './ChatEntry.css';
import TimeStamp from './TimeStamp';
import PropTypes from 'prop-types';

const ChatEntry = (props) => {
const isRemote = props.sender === 'Estragon' ? true : false;
const senderLocation = isRemote
? 'chat-entry remote'
: 'chat-entry local';
const senderColor = isRemote
? props.remoteColor
: props.localColor;
Comment on lines +6 to +12
Copy link
Author

Choose a reason for hiding this comment

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

Ternary Terminal lol!


const ChatEntry = () => {
return (
<div className="chat-entry local">
<h2 className="entry-name">Replace with name of 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>
<div className={senderLocation}>
<section>
<h2 className="entry-name">{props.sender}</h2>
<section className="entry-bubble">
<p className={senderColor}>{props.body}</p>
<p className="entry-time"><TimeStamp time={props.timeStamp} /></p>
<button
className="like"
onClick={
() => props.onMessageLiked(props.id)
}>{props.liked ? '❤️' : '🤍'}
Comment on lines +23 to +25
Copy link
Author

Choose a reason for hiding this comment

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

Nice work!

</button>
</section>
</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.isRequired,
onMessageLiked: PropTypes.func.isRequired,
localColor: PropTypes.string,
remoteColor: PropTypes.string,
Comment on lines +34 to +41
Copy link
Author

Choose a reason for hiding this comment

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

⭐️

};

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

function ChatLog(props) {
const chatMessages = props.entries.map((message) => {
return (
<div key={message.id} className="chat-log">
Copy link
Author

Choose a reason for hiding this comment

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

⭐️

Copy link
Author

Choose a reason for hiding this comment

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

Could we change the tag from div to be more semantic?

<ChatEntry
id={message.id}
sender={message.sender}
body={message.body}
timeStamp={message.timeStamp}
liked={message.liked}
Comment on lines +9 to +14
Copy link
Author

Choose a reason for hiding this comment

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

Since the names of the keys on entry are the same as the names your are setting on ChatEntry attributes/you are using all of the values in entry you could do something something like this to save you a few keystrokes:

  const chatComponents = entries.map((entry) => {
    return(
      <ChatEntry
        {...entry}
        onLikeToggle={onLikeBtnToggle}
        key={entry.id}
      />
    );
  });

onMessageLiked={props.onMessageLiked}
localColor={props.localColor}
remoteColor={props.remoteColor}
/>
</div>
);
});

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,
liked: PropTypes.bool.isRequired,
})).isRequired,
onMessageLiked: PropTypes.func.isRequired,
localColor: PropTypes.string,
remoteColor: PropTypes.string,
Comment on lines +27 to +37
Copy link
Author

Choose a reason for hiding this comment

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

⭐️

};

export default ChatLog;