diff --git a/src/App.css b/src/App.css index d97beb4e..6867fbf5 100644 --- a/src/App.css +++ b/src/App.css @@ -2,73 +2,9 @@ background-color: #87cefa; } -#App header { - background-color: #222; - color: #fff; - padding-bottom: 0.5rem; - position: fixed; - width: 100%; - z-index: 100; - text-align: center; - align-items: center; -} - #App main { padding-left: 2em; padding-right: 2em; padding-bottom: 5rem; padding-top: 10rem; -} - -#App h1 { - font-size: 1.5em; - text-align: center; - display: inline-block; -} - -#App header section { - background-color: #e0ffff; -} - -#App .widget { - display: inline-block; - line-height: 0.5em; - border-radius: 10px; - color: black; - font-size:0.8em; - padding-left: 1em; - padding-right: 1em; -} - -#App #heartWidget { - font-size: 1.5em; - margin: 1em -} - -#App span { - display: inline-block -} - -.red { - color: #b22222 -} - -.orange { - color: #e6ac00 -} - -.yellow { - color: #e6e600 -} - -.green { - color: green -} - -.blue { - color: blue -} - -.purple { - color: purple } \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index 14a7f684..ae74e078 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,14 +1,53 @@ import './App.css'; +import { useState } from 'react'; +import ChatLog from './components/ChatLog.jsx'; +import messages from './data/messages.json'; +import Header from './components/Header.jsx'; const App = () => { + const [entries, setEntries] = useState(messages); + const [fontColorForLocalSender, setFontColorForLocalSender] = useState('black'); + const [fontColorForRemoteSender, setFontColorForRemoteSender] = useState('black'); + const localSender = 'Vladimir'; + + const totalLikes = entries.reduce((total, entry) => { + return entry.isLiked ? total + 1 : total; + }, 0); + + const toggleHeart = (entryId) => { + setEntries(entries => { + return entries.map(entry => { + if (entry.id === entryId) { + return { ...entry, isLiked: !entry.isLiked }; + } else { + return entry; + } + }); + }); + }; + + const chooseFontColor = (senderName, color) => { + if (senderName === localSender) { + setFontColorForLocalSender(color); + } else { + setFontColorForRemoteSender(color); + } + }; + return (
-
-

Application title

-
+
- {/* Wave 01: Render one ChatEntry component - Wave 02: Render ChatLog component */} +
); diff --git a/src/components/ChatEntry.css b/src/components/ChatEntry.css index 05c3baa4..40f6c4d1 100644 --- a/src/components/ChatEntry.css +++ b/src/components/ChatEntry.css @@ -2,7 +2,7 @@ button { background: none; color: inherit; border: none; - padding: 10px; + padding: 10px; font: inherit; cursor: pointer; outline: inherit; @@ -97,4 +97,18 @@ button { .chat-entry.remote .entry-bubble:hover::before { background-color: #a9f6f6; +} + +.entry-attributes { + display: flex; + align-items: center; + justify-content: space-between; +} + +.like { + padding: 0; + width: 20px; + height: 20px; + align-items: center; + line-height: 1; } \ No newline at end of file diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index fe05efa0..09ecc262 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -1,21 +1,40 @@ import './ChatEntry.css'; +import PropTypes from 'prop-types'; +import TimeStamp from './TimeStamp'; + +const ChatEntry = (props) => { + const isLocal = props.sender === props.localSender; + const entryClass = isLocal ? 'local' : 'remote'; + const isLiked = props.isLiked ? '❤️' : '🤍'; + const isLikedButtonClicked = () => { + props.onToggleHeart(props.id); + }; + + const fontColor = isLocal ? props.fontColorForLocalSender : props.fontColorForRemoteSender; -const ChatEntry = () => { return ( - // Replace the outer tag name with a semantic element that fits our use case - -

Replace with name of sender

+
+

{props.sender}

-

Replace with body of ChatEntry

-

Replace with TimeStamp component

- +

{props.body}

+
+

+ +

+ +
- +
); }; -ChatEntry.propTypes = { - // Fill with correct proptypes -}; - export default ChatEntry; + +ChatEntry.propTypes = { + sender: PropTypes.string.isRequired, + body: PropTypes.string.isRequired, + timeStamp: PropTypes.string.isRequired, + localSender: PropTypes.string, + isLiked: PropTypes.bool, + onToggleHeart: PropTypes.func, +}; \ No newline at end of file diff --git a/src/components/ChatLog.jsx b/src/components/ChatLog.jsx new file mode 100644 index 00000000..d98a5de3 --- /dev/null +++ b/src/components/ChatLog.jsx @@ -0,0 +1,39 @@ +import './ChatLog.css'; +import ChatEntry from './ChatEntry'; +import PropTypes from 'prop-types'; + +const ChatLog = ({entries, localSender, onToggleHeart, fontColorForLocalSender, fontColorForRemoteSender}) => { + return ( +
+ {entries.map((entry) => ( + + ))} +
+ ); +}; + +export default ChatLog; + +ChatLog.propTypes = { + entries: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.number.isRequired, + sender: PropTypes.string.isRequired, + body: PropTypes.string.isRequired, + timeStamp: PropTypes.string.isRequired, + }) + ).isRequired, + localSender: PropTypes.string, + onToggleHeart: PropTypes.func, +}; \ No newline at end of file diff --git a/src/components/Header.css b/src/components/Header.css new file mode 100644 index 00000000..217e9d7d --- /dev/null +++ b/src/components/Header.css @@ -0,0 +1,20 @@ +#App header { + background-color: #2c2c2c; + color: #fff; + padding-bottom: 0.5rem; + position: fixed; + width: 100%; + z-index: 100; + text-align: center; + align-items: center; +} + +#App h1 { + font-size: 1.5em; + text-align: center; + display: inline-block; +} + +#App header section { + background-color: #e0ffff; +} \ No newline at end of file diff --git a/src/components/Header.jsx b/src/components/Header.jsx new file mode 100644 index 00000000..5f8c31b6 --- /dev/null +++ b/src/components/Header.jsx @@ -0,0 +1,34 @@ +import { useState } from 'react'; +import './Header.css'; +import HeartCounter from './HeartCounter.jsx'; +import SenderColorPicker from './SenderColorPicker.jsx'; + +const Header = ({ totalLikes, chooseFontColor }) => { + const [localColor, setLocalColor] = useState('white'); + const [remoteColor, setRemoteColor] = useState('white'); + + const changeFontColor = (senderName, color) => { + if (senderName === 'Vladimir') { + setLocalColor(color); + } else { + setRemoteColor(color); + } + chooseFontColor(senderName, color); + }; + + return ( +
+

+ Chat between Vladimir and Estragon +

+ +
+ + + +
+
+ ); +}; + +export default Header; \ No newline at end of file diff --git a/src/components/HeartCounter.css b/src/components/HeartCounter.css new file mode 100644 index 00000000..e02387f2 --- /dev/null +++ b/src/components/HeartCounter.css @@ -0,0 +1,18 @@ +#App .widget { + display: inline-block; + line-height: 0.5em; + border-radius: 10px; + color: black; + font-size: 0.8em; + padding-left: 1em; + padding-right: 1em; +} + +#App #heartWidget { + font-size: 1.5em; + margin: 1em; +} + +#App span { + display: inline-block; +} \ No newline at end of file diff --git a/src/components/HeartCounter.jsx b/src/components/HeartCounter.jsx new file mode 100644 index 00000000..d3fc3673 --- /dev/null +++ b/src/components/HeartCounter.jsx @@ -0,0 +1,11 @@ +import './HeartCounter.css'; + +const HeartCounter = ({ totalLikes }) => { + return ( +
+ {totalLikes} ❤️s +
+ ); +}; + +export default HeartCounter; diff --git a/src/components/SenderColorPicker.css b/src/components/SenderColorPicker.css new file mode 100644 index 00000000..53fc8d60 --- /dev/null +++ b/src/components/SenderColorPicker.css @@ -0,0 +1,35 @@ +span { + display: inline-block +} + +.red { + color: #b22222 +} + +.orange { + color: #e6ac00 +} + +.yellow { + color: #e6e600 +} + +.green { + color: green +} + +.blue { + color: blue +} + +.purple { + color: purple +} + +.black { + color: black +} + +.senderColorPicker span { + margin: 0 0.15em; +} \ No newline at end of file diff --git a/src/components/SenderColorPicker.jsx b/src/components/SenderColorPicker.jsx new file mode 100644 index 00000000..24626fe8 --- /dev/null +++ b/src/components/SenderColorPicker.jsx @@ -0,0 +1,29 @@ +import { useState } from 'react'; +import './SenderColorPicker.css'; + +const SenderColorPicker = ({ senderName, chooseFontColor }) => { + + const [color, setColor] = useState('black'); + + const colorButtonClicked = (color) => { + chooseFontColor(senderName, color); + setColor(color); + }; + + return ( +
+

{senderName}'s color:

+ + + + + + + + + +
+ ); +}; + +export default SenderColorPicker; \ No newline at end of file