diff --git a/src/App.css b/src/App.css index d97beb4e6..8c8ae257d 100644 --- a/src/App.css +++ b/src/App.css @@ -1,52 +1,133 @@ #App { - background-color: #87cefa; -} + background-color: white; -#App header { - background-color: #222; - color: #fff; - padding-bottom: 0.5rem; - position: fixed; - width: 100%; - z-index: 100; - text-align: center; - align-items: center; -} + header { + background-color: #e9ecef; + color: black; + 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; -} + 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; -} + h1 { + font-size: 1.5em; + text-align: center; + display: inline-block; + padding-top: 0.5em; + } -#App header section { - background-color: #e0ffff; -} + 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; -} + .widget { + display: inline-block; + line-height: 0.5em; + border-radius: 10px; + color: black; + font-size:0.8em; + padding: 1em; + font-weight: bold; + } + + #heartWidget { + display: flex; + align-items: center; + justify-content: center; + font-size: 1.5em; + margin: 0.25em; + } -#App #heartWidget { - font-size: 1.5em; - margin: 1em + span { + display: inline-block; + } } -#App span { - display: inline-block +#AppDark { + background-color: #343a40; + + header { + background-color: #222; + color: #fff; + position: fixed; + width: 100%; + z-index: 100; + text-align: center; + align-items: center; + } + + main { + padding-left: 2em; + padding-right: 2em; + padding-bottom: 5rem; + padding-top: 10rem; + } + + h1 { + font-size: 1.5em; + text-align: center; + display: inline-block; + padding-top: 0.5em; + } + + h2 { + color: white; + } + + .entry-bubble p { + color: white; + } + + .local .entry-bubble { + background-color: #212529; + } + + .local .entry-bubble::before { + background-color: #212529; + } + + .remote .entry-bubble { + background-color: #2a6f97; + } + + .remote .entry-bubble::before { + background-color: #2a6f97; + } + + header section { + background-color: #00568a; + } + + .widget { + display: inline-block; + line-height: 0.5em; + border-radius: 10px; + color: white; + font-size:0.8em; + padding: 1em; + font-weight: bold; + } + + #heartWidget { + display: flex; + align-items: center; + justify-content: center; + font-size: 1.5em; + margin: 0.25em; + } + + span { + display: inline-block + } } .red { diff --git a/src/App.jsx b/src/App.jsx index 14a7f684d..161a2fdd1 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,14 +1,41 @@ import './App.css'; +import CHATS from './data/messages.json'; +import ChatLog from './components/ChatLog'; +import { useState } from 'react'; +import DarkMode from './dark-mode/DarkMode'; + const App = () => { + const [chatData, setChatData] = useState(CHATS); + const [theme, setTheme] = useState('App'); + + const handleLikedMessages = (id) => { + setChatData(chatData => chatData.map(chat => { + if (chat.id === id) { + return { ...chat, liked: !chat.liked }; + } else { + return chat; + } + })); + }; + + const toggleDarkMode = (mode) => { + setTheme(mode); + }; + + const likedCount = chatData.filter((chat) => chat.liked).length; + return ( -
+
-

Application title

+

Chatroom: {chatData[0].sender} and {chatData[1].sender}

+
+ {likedCount} ❤️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 15c56f96b..2d500d3bb 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -1,20 +1,39 @@ import './ChatEntry.css'; +import PropTypes from 'prop-types'; +import TimeStamp from './TimeStamp'; + +const ChatEntry = ({ id, sender, body, timeStamp, liked, onLiked }) => { + const onHeartClick = () => { + onLiked(id); + }; + + const filledHeart = liked ? '❤️' : '🤍'; + + const setMessageBubble = id%2 ? 'local' : 'remote'; -const ChatEntry = () => { return ( -
-

Replace with name of sender

-
-

Replace with body of ChatEntry

-

Replace with TimeStamp component

- -
-
+ <> +
+

{sender}

+
+

{body}

+

+ +

+ +
+
+ ); }; 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, + onLiked: PropTypes.func.isRequired, }; export default ChatEntry; diff --git a/src/components/ChatLog.jsx b/src/components/ChatLog.jsx new file mode 100644 index 000000000..ce6c49c88 --- /dev/null +++ b/src/components/ChatLog.jsx @@ -0,0 +1,37 @@ +import PropTypes from 'prop-types'; +import ChatEntry from './ChatEntry'; + +const ChatLog = ({ entries, onLiked }) => { + const chatEntryComponents = entries.map((entry) => { + return ( + + ); + }); + + return ( + + ); +}; + +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, + onLiked: PropTypes.func.isRequired, +}; + +export default ChatLog; diff --git a/src/dark-mode/DarkMode.css b/src/dark-mode/DarkMode.css new file mode 100644 index 000000000..bd4db5b3a --- /dev/null +++ b/src/dark-mode/DarkMode.css @@ -0,0 +1,44 @@ +.dark_mode { + margin-top: -20px; + margin-left: 10px; +} + +.dark_mode_label { + width: 55px; + height: 30px; + position: relative; + display: block; + background: #ebebeb; + border-radius: 200px; + box-shadow: inset 0px 5px 15px rgba(0, 0, 0, 0.4), + inset 0px -5px 15px rgba(255, 255, 255, 0.4); + cursor: pointer; + transition: 0.3s; +} +.dark_mode_label:after { + content: ""; + width: 25px; + height: 25px; + position: absolute; + top: 3px; + left: 5px; + background: linear-gradient(180deg, #f8f9fa, #e9ecef); + border-radius: 180px; + box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.2); + transition: 0.3s; +} +.dark_mode_input { + width: 0; + height: 0; + visibility: hidden; +} +.dark_mode_input:checked + .dark_mode_label { + background: #242424; +} +.dark_mode_input:checked + .dark_mode_label:after { + left: 50px; + transform: translateX(-100%); +} +.dark_mode_label:active:after { + width: 30px; +} diff --git a/src/dark-mode/DarkMode.jsx b/src/dark-mode/DarkMode.jsx new file mode 100644 index 000000000..b62659ec6 --- /dev/null +++ b/src/dark-mode/DarkMode.jsx @@ -0,0 +1,29 @@ +import './DarkMode.css'; +import PropTypes from 'prop-types'; + +const DarkMode = ({ currentTheme, toggleTheme }) => { + let mode = currentTheme === 'App' ? 'AppDark' : 'App'; + + return ( +
+ + +
+ ); +}; + +DarkMode.propTypes = { + currentTheme: PropTypes.string.isRequired, + toggleTheme: PropTypes.func.isRequired +}; + +export default DarkMode;