diff --git a/src/App.css b/src/App.css index d97beb4e6..42e0e74ea 100644 --- a/src/App.css +++ b/src/App.css @@ -49,26 +49,30 @@ display: inline-block } +#App .widget button { + border-radius: 80%; +} + .red { - color: #b22222 + color: #b22222; } .orange { - color: #e6ac00 + color: #e6ac00; } .yellow { - color: #e6e600 + color: #e6e600; } .green { - color: green + color: green; } .blue { - color: blue + color: blue; } .purple { - color: purple + color: purple; } \ No newline at end of file diff --git a/src/App.js b/src/App.js index c10859093..b969ef8fe 100644 --- a/src/App.js +++ b/src/App.js @@ -1,16 +1,87 @@ -import React from 'react'; +import React, { useState } from 'react'; import './App.css'; +import ChatLog from './components/ChatLog'; import chatMessages from './data/messages.json'; +import ChatMessage from './models/ChatMessage'; +import ColorChoice from './components/ColorChoice'; + +const messages = []; +let isLocal = true; +let initialLikes = 0; +for (const msg of chatMessages) { + messages.push( + new ChatMessage( + msg.id, + msg.sender, + msg.body, + msg.timeStamp, + msg.liked, + isLocal + ) + ); + if (msg.liked) { + ++initialLikes; + } + isLocal = !isLocal; +} const App = () => { + const [chatEntries, setChatEntries] = useState(messages); + const [localColor, setLocalColor] = useState(''); + const [remoteColor, setRemoteColor] = useState(''); + const [totalLikes, setTotalLikes] = useState(initialLikes); + + const updateChatEntry = (updatedEntry) => { + const updatedMsg = chatEntries.map((entry) => { + if (entry.id === updatedEntry.id) { + if (updatedEntry.liked) { + setTotalLikes(totalLikes + 1); + } else { + setTotalLikes(totalLikes - 1); + } + return updatedEntry; + } else return entry; + }); + setChatEntries(updatedMsg); + }; + + const getColor = (local) => { + if (local) { + return localColor; + } + return remoteColor; + }; + return (
-

Application title

+

+ Chat Between{' '} + {chatEntries[0].sender} and{' '} + {chatEntries[1].sender} +

+
+ +

+ {totalLikes} ❤️s +

+ +
- {/* Wave 01: Render one ChatEntry component - Wave 02: Render ChatLog component */} +
); diff --git a/src/components/ChatEntry.js b/src/components/ChatEntry.js index b92f0b7b2..97d93c6dc 100644 --- a/src/components/ChatEntry.js +++ b/src/components/ChatEntry.js @@ -1,22 +1,46 @@ -import React from 'react'; import './ChatEntry.css'; import PropTypes from 'prop-types'; +import TimeStamp from './TimeStamp'; +import ChatMessage from '../models/ChatMessage'; + +const ChatEntry = ({ message, onUpdateEntry, getColor }) => { + const typeHeart = message.liked ? '❤️' : '🤍'; + const isLocalclass = message.isLocal ? 'local' : 'remote'; + const colorClass = getColor(message.isLocal); + + const clickLike = (e) => { + onUpdateEntry( + new ChatMessage( + message.id, + message.sender, + message.body, + message.timeStamp, + !message.liked, + message.isLocal + ) + ); + }; -const ChatEntry = (props) => { return ( -
-

Replace with name of sender

+
+

{message.sender}

-

Replace with body of ChatEntry

-

Replace with TimeStamp component

- +

{message.body}

+

+ +

+
); }; ChatEntry.propTypes = { - //Fill with correct proptypes + message: PropTypes.instanceOf(ChatMessage).isRequired, + onUpdateEntry: PropTypes.func.isRequired, + getColor: PropTypes.func, }; export default ChatEntry; diff --git a/src/components/ChatEntry.test.js b/src/components/ChatEntry.test.js index b69270c03..a4a84fe82 100644 --- a/src/components/ChatEntry.test.js +++ b/src/components/ChatEntry.test.js @@ -1,28 +1,35 @@ -import React from "react"; -import "@testing-library/jest-dom/extend-expect"; -import ChatEntry from "./ChatEntry"; -import { render, screen, fireEvent, waitFor } from "@testing-library/react"; +import React from 'react'; +import '@testing-library/jest-dom/extend-expect'; +import ChatEntry from './ChatEntry'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import ChatMessage from '../models/ChatMessage'; -describe("Wave 01: ChatEntry", () => { +describe('Wave 01: ChatEntry', () => { beforeEach(() => { render( ); }); - test("renders without crashing and shows the sender", () => { + test('renders without crashing and shows the sender', () => { expect(screen.getByText(/Joe Biden/)).toBeInTheDocument(); }); - test("that it will display the body", () => { + test('that it will display the body', () => { expect(screen.getByText(/Get out by 8am/)).toBeInTheDocument(); }); - test("that it will display the time", () => { + test('that it will display the time', () => { expect(screen.getByText(/\d+ years ago/)).toBeInTheDocument(); }); }); diff --git a/src/components/ChatLog.css b/src/components/ChatLog.css index 378848d1f..89f6b6bd4 100644 --- a/src/components/ChatLog.css +++ b/src/components/ChatLog.css @@ -2,3 +2,7 @@ margin: auto; max-width: 50rem; } + +.chat-entries-list { + list-style: none; +} diff --git a/src/components/ChatLog.js b/src/components/ChatLog.js new file mode 100644 index 000000000..715e70d2d --- /dev/null +++ b/src/components/ChatLog.js @@ -0,0 +1,33 @@ +import React from 'react'; +import ChatEntry from './ChatEntry'; +import './ChatLog.css'; +import PropTypes from 'prop-types'; +import ChatMessage from '../models/ChatMessage'; + +const ChatLog = (props) => { + const chatEntries = props.entries.map((entry) => { + return ( +
  • + +
  • + ); + }); + + return ( +
    +
      {chatEntries}
    +
    + ); +}; + +ChatLog.propTypes = { + entries: PropTypes.arrayOf(PropTypes.instanceOf(ChatMessage)).isRequired, + onUpdateChatEntry: PropTypes.func.isRequired, + //getColor: PropTypes.func, +}; + +export default ChatLog; diff --git a/src/components/ChatLog.test.js b/src/components/ChatLog.test.js index 96f89ebc3..5bafee291 100644 --- a/src/components/ChatLog.test.js +++ b/src/components/ChatLog.test.js @@ -1,49 +1,49 @@ -import React from "react"; -import "@testing-library/jest-dom/extend-expect"; -import ChatLog from "./ChatLog"; -import { render, screen } from "@testing-library/react"; +import React from 'react'; +import '@testing-library/jest-dom/extend-expect'; +import ChatLog from './ChatLog'; +import { render, screen } from '@testing-library/react'; const LOG = [ { - sender: "Vladimir", - body: "why are you arguing with me", - timeStamp: "2018-05-29T22:49:06+00:00", + sender: 'Vladimir', + body: 'why are you arguing with me', + timeStamp: '2018-05-29T22:49:06+00:00', }, { - sender: "Estragon", - body: "Because you are wrong.", - timeStamp: "2018-05-29T22:49:33+00:00", + sender: 'Estragon', + body: 'Because you are wrong.', + timeStamp: '2018-05-29T22:49:33+00:00', }, { - sender: "Vladimir", - body: "because I am what", - timeStamp: "2018-05-29T22:50:22+00:00", + sender: 'Vladimir', + body: 'because I am what', + timeStamp: '2018-05-29T22:50:22+00:00', }, { - sender: "Estragon", - body: "A robot.", - timeStamp: "2018-05-29T22:52:21+00:00", + sender: 'Estragon', + body: 'A robot.', + timeStamp: '2018-05-29T22:52:21+00:00', }, { - sender: "Vladimir", - body: "Notabot", - timeStamp: "2019-07-23T22:52:21+00:00", + sender: 'Vladimir', + body: 'Notabot', + timeStamp: '2019-07-23T22:52:21+00:00', }, ]; -describe("Wave 02: ChatLog", () => { +describe('Wave 02: ChatLog', () => { beforeEach(() => { render(); }); - test("renders without crashing and shows all the names", () => { + test('renders without crashing and shows all the names', () => { [ { - name: "Vladimir", + name: 'Vladimir', numChats: 3, }, { - name: "Estragon", + name: 'Estragon', numChats: 2, }, ].forEach((person) => { @@ -56,7 +56,7 @@ describe("Wave 02: ChatLog", () => { }); }); - test("renders an empty list without crashing", () => { + test('renders an empty list without crashing', () => { const element = render(); expect(element).not.toBeNull(); }); diff --git a/src/components/ColorChoice.css b/src/components/ColorChoice.css new file mode 100644 index 000000000..48ff13cd8 --- /dev/null +++ b/src/components/ColorChoice.css @@ -0,0 +1,23 @@ +button.red { + background-color: #b22222; + } + +button.orange { + background-color: #e6ac00; + } + +button.yellow { + background-color: #e6e600; + } + +button.green { + background-color: green; + } + +button.blue { + background-color: blue; + } + +button.purple { + background-color: purple; + } \ No newline at end of file diff --git a/src/components/ColorChoice.js b/src/components/ColorChoice.js new file mode 100644 index 000000000..b7d6b2efd --- /dev/null +++ b/src/components/ColorChoice.js @@ -0,0 +1,36 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const ColorChoice = ({ senderName, newColor, updateColor }) => { + return ( +
    +

    {senderName}'s color:

    + + + + + + +
    + ); +}; + +ColorChoice.propTypes = { + senderName: PropTypes.string.isRequired, + newColor: PropTypes.string.isRequired, + updateColor: PropTypes.func.isRequired, +}; + +export default ColorChoice; diff --git a/src/models/ChatMessage.js b/src/models/ChatMessage.js new file mode 100644 index 000000000..f6c8e4eb4 --- /dev/null +++ b/src/models/ChatMessage.js @@ -0,0 +1,12 @@ +class ChatMessage { + constructor(id, sender, body, timeStamp, liked, isLocal, color) { + this.id = id; + this.sender = sender; + this.body = body; + this.timeStamp = timeStamp; + this.liked = liked; + this.isLocal = isLocal; + } +} + +export default ChatMessage;