From f12bf7a8c5085052ed755b095b3505ce7f8ae288 Mon Sep 17 00:00:00 2001
From: Kristina N <87397396+kstinanguyen@users.noreply.github.com>
Date: Wed, 11 Dec 2024 13:53:39 -0800
Subject: [PATCH 1/4] wave 1 and wave 2 implemented
---
src/App.jsx | 3 +++
src/components/ChatEntry.jsx | 28 ++++++++++++++++++----------
src/components/ChatLog.jsx | 31 +++++++++++++++++++++++++++++++
3 files changed, 52 insertions(+), 10 deletions(-)
create mode 100644 src/components/ChatLog.jsx
diff --git a/src/App.jsx b/src/App.jsx
index 14a7f684d..c09b9f3f0 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,4 +1,6 @@
import './App.css';
+import chatData from './data/messages.json';
+import ChatLog from './components/ChatLog';
const App = () => {
return (
@@ -9,6 +11,7 @@ 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 15c56f96b..d3759eb3c 100644
--- a/src/components/ChatEntry.jsx
+++ b/src/components/ChatEntry.jsx
@@ -1,20 +1,28 @@
import './ChatEntry.css';
+import PropTypes from 'prop-types';
+import TimeStamp from './TimeStamp';
-const ChatEntry = () => {
+const ChatEntry = ({ sender, body, timeStamp }) => {
return (
-
-
Replace with name of sender
-
- Replace with body of ChatEntry
- Replace with TimeStamp component
-
-
-
+ <>
+
+ >
);
};
ChatEntry.propTypes = {
- // Fill with correct proptypes
+ sender: PropTypes.string.isRequired,
+ body: PropTypes.string.isRequired,
+ timeStamp: PropTypes.string.isRequired,
};
export default ChatEntry;
diff --git a/src/components/ChatLog.jsx b/src/components/ChatLog.jsx
new file mode 100644
index 000000000..f47703888
--- /dev/null
+++ b/src/components/ChatLog.jsx
@@ -0,0 +1,31 @@
+import PropTypes from 'prop-types';
+import ChatEntry from './ChatEntry';
+
+const ChatLog = ({ entries }) => {
+ const chatEntryComponents = entries.map((entry) => {
+ return (
+
+ );
+ });
+
+ return (
+
+ {chatEntryComponents}
+
+ );
+};
+
+ChatLog.propTypes = {
+ entries: PropTypes.arrayOf(PropTypes.shape({
+ sender: PropTypes.string.isRequired,
+ body: PropTypes.string.isRequired,
+ timeStamp: PropTypes.string.isRequired,
+ })).isRequired
+};
+
+export default ChatLog;
From 19a93410b63786fe5f2de23ab2b8bf3e636f895c Mon Sep 17 00:00:00 2001
From: Kristina N <87397396+kstinanguyen@users.noreply.github.com>
Date: Thu, 12 Dec 2024 16:28:53 -0800
Subject: [PATCH 2/4] wave 3 implemented, all tests passing
---
src/App.css | 6 ++----
src/App.jsx | 24 +++++++++++++++++++++---
src/components/ChatEntry.jsx | 13 +++++++++++--
src/components/ChatLog.jsx | 10 ++++++++--
4 files changed, 42 insertions(+), 11 deletions(-)
diff --git a/src/App.css b/src/App.css
index d97beb4e6..6ff25f541 100644
--- a/src/App.css
+++ b/src/App.css
@@ -5,7 +5,6 @@
#App header {
background-color: #222;
color: #fff;
- padding-bottom: 0.5rem;
position: fixed;
width: 100%;
z-index: 100;
@@ -36,13 +35,12 @@
border-radius: 10px;
color: black;
font-size:0.8em;
- padding-left: 1em;
- padding-right: 1em;
+ padding: 1em;
}
#App #heartWidget {
font-size: 1.5em;
- margin: 1em
+ margin: 1em;
}
#App span {
diff --git a/src/App.jsx b/src/App.jsx
index c09b9f3f0..c7caae800 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,17 +1,35 @@
import './App.css';
-import chatData from './data/messages.json';
+import CHATS from './data/messages.json';
import ChatLog from './components/ChatLog';
+import { useState } from 'react';
const App = () => {
+ const [chatData, setChatData] = useState(CHATS);
+
+ const handleLikedMessages = (id) => {
+ setChatData(chatData => chatData.map(chat => {
+ if (chat.id === id) {
+ return { ...chat, liked: !chat.liked };
+ } else {
+ return chat;
+ }
+ }));
+ };
+
+ const likedCount = chatData.filter((chat) => chat.liked).length;
+
return (
- Application title
+ Chat thread between Name1 and Name2
+
{/* Wave 01: Render one ChatEntry component
Wave 02: Render ChatLog component */}
-
+
);
diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx
index d3759eb3c..ac3926366 100644
--- a/src/components/ChatEntry.jsx
+++ b/src/components/ChatEntry.jsx
@@ -2,7 +2,13 @@ import './ChatEntry.css';
import PropTypes from 'prop-types';
import TimeStamp from './TimeStamp';
-const ChatEntry = ({ sender, body, timeStamp }) => {
+const ChatEntry = ({ id, sender, body, timeStamp, liked, onLiked }) => {
+ const onHeartClick = () => {
+ onLiked(id);
+ };
+
+ const filledHeart = liked ? '❤️' : '🤍';
+
return (
<>
@@ -12,7 +18,7 @@ const ChatEntry = ({ sender, body, timeStamp }) => {
-
+
>
@@ -20,9 +26,12 @@ 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,
+ onLiked: PropTypes.func.isRequired,
};
export default ChatEntry;
diff --git a/src/components/ChatLog.jsx b/src/components/ChatLog.jsx
index f47703888..ce6c49c88 100644
--- a/src/components/ChatLog.jsx
+++ b/src/components/ChatLog.jsx
@@ -1,13 +1,16 @@
import PropTypes from 'prop-types';
import ChatEntry from './ChatEntry';
-const ChatLog = ({ entries }) => {
+const ChatLog = ({ entries, onLiked }) => {
const chatEntryComponents = entries.map((entry) => {
return (
);
@@ -22,10 +25,13 @@ const ChatLog = ({ entries }) => {
ChatLog.propTypes = {
entries: PropTypes.arrayOf(PropTypes.shape({
+ id: PropTypes.number.isRequired,
sender: PropTypes.string.isRequired,
body: PropTypes.string.isRequired,
timeStamp: PropTypes.string.isRequired,
- })).isRequired
+ liked: PropTypes.bool.isRequired,
+ })).isRequired,
+ onLiked: PropTypes.func.isRequired,
};
export default ChatLog;
From b71053def7c1dc1940aa5c040fa3485cb3bb6c7a Mon Sep 17 00:00:00 2001
From: Kristina N <87397396+kstinanguyen@users.noreply.github.com>
Date: Thu, 12 Dec 2024 16:54:57 -0800
Subject: [PATCH 3/4] implemented optional enhancement: local and remote
bubbles
---
src/App.css | 4 +++-
src/App.jsx | 4 +---
src/components/ChatEntry.jsx | 4 +++-
3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/src/App.css b/src/App.css
index 6ff25f541..6dedb9799 100644
--- a/src/App.css
+++ b/src/App.css
@@ -23,6 +23,7 @@
font-size: 1.5em;
text-align: center;
display: inline-block;
+ padding-top: 0.5em;
}
#App header section {
@@ -36,11 +37,12 @@
color: black;
font-size:0.8em;
padding: 1em;
+ font-weight: bold;
}
#App #heartWidget {
font-size: 1.5em;
- margin: 1em;
+ margin: 0.25em;
}
#App span {
diff --git a/src/App.jsx b/src/App.jsx
index c7caae800..bcb5ca361 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -21,14 +21,12 @@ const App = () => {
return (
- Chat thread between Name1 and Name2
+ Chatroom: Name1 and Name2
- {/* Wave 01: Render one ChatEntry component
- Wave 02: Render ChatLog component */}
diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx
index ac3926366..2d500d3bb 100644
--- a/src/components/ChatEntry.jsx
+++ b/src/components/ChatEntry.jsx
@@ -9,9 +9,11 @@ const ChatEntry = ({ id, sender, body, timeStamp, liked, onLiked }) => {
const filledHeart = liked ? '❤️' : '🤍';
+ const setMessageBubble = id%2 ? 'local' : 'remote';
+
return (
<>
-
+
{sender}
{body}
From 8c766cb1d9763891e6869a447b2b2189b04e682e Mon Sep 17 00:00:00 2001
From: Kristina N <87397396+kstinanguyen@users.noreply.github.com>
Date: Mon, 16 Dec 2024 00:19:38 -0800
Subject: [PATCH 4/4] additional enhancements for light-dark theme toggle
---
src/App.css | 161 ++++++++++++++++++++++++++++---------
src/App.jsx | 12 ++-
src/dark-mode/DarkMode.css | 44 ++++++++++
src/dark-mode/DarkMode.jsx | 29 +++++++
4 files changed, 204 insertions(+), 42 deletions(-)
create mode 100644 src/dark-mode/DarkMode.css
create mode 100644 src/dark-mode/DarkMode.jsx
diff --git a/src/App.css b/src/App.css
index 6dedb9799..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;
- 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;
- padding-top: 0.5em;
-}
+ 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: 1em;
- font-weight: bold;
-}
+ .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: 0.25em;
+ 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 bcb5ca361..161a2fdd1 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -2,9 +2,12 @@ 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 => {
@@ -16,14 +19,19 @@ const App = () => {
}));
};
+ const toggleDarkMode = (mode) => {
+ setTheme(mode);
+ };
+
const likedCount = chatData.filter((chat) => chat.liked).length;
return (
-
+
- Chatroom: Name1 and Name2
+ Chatroom: {chatData[0].sender} and {chatData[1].sender}
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;