diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..c67fb8f --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,45 @@ +# Design and Build a Chat App with Socket.io + +> ## Watch on [YouTube](https://www.youtube.com/playlist?list=PLDlWc9AfQBfbyGwhSlxg16mQGpGnauCwq) + +## Introduction + +This mini series is jam packed full of useful information for the beginner to intermediate Web Developer. + +### What We'll Cover + +HTML, CSS, JavaScript +ES6 - Template Strings, Map(), Arrow Functions +Flexbox and CSS Variables +NPM, Node, Express, Socket.IO +Web Design with Sketch + +In this mini series we are going to Design and Build a chat application using Socket.io. We will first discuss basic design concepts while exploring the design for this application in Sketch. We will then move on to laying out our application in HTML and CSS, and lastly, will add Javascript to complete the functionality. Along the way, we are going to use modern Web Development technologies and features such as Flexbox, CSS Variables, ES6 Arrow Functions, ES6 Map, Let and Const variables, Node.js, Express.js, and Socket.io. + +## [Part 1](/part-1) - App Design + +In Part 1 of this series, we are going to explore basic design concepts in Web Development like typography, colors, white space, etc. We will explore the design for this demo application, "Quick Chat", in Sketch, one of the most popular design tools available. Additionally, we will look at a helpful resource for testing different color palettes. + +Resources +https://www.sketchapp.com/ +https://www.materialpalette.com/ + +## [Part 2](/part-2) - Creating HTML Structure and CSS Helper Classes + +In Part 2 of this series, we will create the HTML structure for the application as well as stubbing out our CSS with resets, variables, and helper classes. + +## [Parts 3 and 4](/parts-3-and-4) - Styling and Laying Out Our App + +In Parts 3 and 4 of this series we will begin to style and organize our application. We will stub out dummy message data, create our login and message forms, and align everything using flexbox. Lastly, we will hide the chat window initially so that we can require the user to login to access the chat window. + +## [Part 5 and 6](/parts-5-and-6) - Adding Functionality in Javascript + +In Parts 5 and 6 of this series, we will dynamically create the HTML for each message in JavaScript by using ES6 template strings and the Map function. We will create the event handlers for our login and message buttons as well as simulating page navigation by hiding/showing login and chat windows appropriately. + +## [Part 7](/part-7)- Create Node server and Socket.io Connection + +In Part 7 of this series, we will initialize our code as a Node application by using NPM and install Express and Socket.io as dependencies. We will move the existing HTML, CSS, and JS files we have been working on to a public directory as well as set up the server to serve our application using Express. Lastly, we will add the necessary code to the server and front-end application to register the socket connection with Socket.io. + +## [Part 8](/part-8) - Complete Socket.io Integration for Real-Time Messaging + +In Part 8 of this series, we will update the event handler for the send message button to send the new message to the server using the Socket.io socket connection. The server will then broadcast the message to each client that has registered a connection. In other words, the new message will be broadcasted to each user logged into the application. Lastly, we will update the date property of each message to be more human readable. diff --git a/part-1/app-design.sketch b/part-1/app-design.sketch new file mode 100644 index 0000000..f04f4ed Binary files /dev/null and b/part-1/app-design.sketch differ diff --git a/part-2/app.css b/part-2/app.css new file mode 100644 index 0000000..665d81e --- /dev/null +++ b/part-2/app.css @@ -0,0 +1,78 @@ +/* Variabls */ +:root { + --primary-blue: #2196f3; + --dark-blue: #1976d2; + --light-blue: #bbdefb; + --white: #ffffff; + --primary-text: #212121; + --secondary-text: #757575; + --light-gray: #bdbdbd; +} + +/* Resets */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Helvetica, sans-serifs; + color: var(--primary-text); +} + +/* Helper Classes */ + +/* Layout/Flexbox */ +.flex { + display: flex; +} + +.flex-center { + justify-content: center; + align-items: center; +} + +.flex-column { + display: flex; + flex-direction: column; +} + +.flex-grow-1 { + flex-grow: 1; +} + +.container { + max-width: 600px; + margin: 0 auto; +} + +.hidden { + display: none; +} + +.mb-2 { + margin-bottom: 20px; +} + +/* Text Classes */ +.primary-text { + color: var(--primary-text); +} + +.secondary-text { + color: var(--secondary-text); +} + +.light-text { + color: var(--light-blue); +} + +.text-center { + text-align: center; +} + +h1 { + font-size: 48px; + font-weight: 300; +} diff --git a/part-2/index.html b/part-2/index.html new file mode 100644 index 0000000..64388f4 --- /dev/null +++ b/part-2/index.html @@ -0,0 +1,50 @@ + + + + + + + + Quick Chat + + + + + +
+ +
+
+

Quick Chat

+

The easiest way to get your message across.

+
+
+ +
+ +
+

Get Started

+
+ +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+ + + + \ No newline at end of file diff --git a/part-7/package.json b/part-7/package.json new file mode 100644 index 0000000..2ae99c2 --- /dev/null +++ b/part-7/package.json @@ -0,0 +1,15 @@ +{ + "name": "quick-chat", + "version": "1.0.0", + "description": "quick chat", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "James Q Quick", + "license": "ISC", + "dependencies": { + "express": "^4.16.3", + "socket.io": "^2.1.0" + } +} diff --git a/part-7/public/app.css b/part-7/public/app.css new file mode 100644 index 0000000..c3bf895 --- /dev/null +++ b/part-7/public/app.css @@ -0,0 +1,188 @@ +/* Variabls */ +:root { + --primary-blue: #2196f3; + --dark-blue: #1976d2; + --light-blue: #bbdefb; + --white: #ffffff; + --primary-text: #212121; + --secondary-text: #757575; + --light-gray: #bdbdbd; +} + +/* Resets */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Helvetica, sans-serifs; + color: var(--primary-text); +} + +/* Helper Classes */ + +/* Layout/Flexbox */ +.flex { + display: flex; +} + +.flex-center { + justify-content: center; + align-items: center; +} + +.flex-column { + display: flex; + flex-direction: column; +} + +.flex-grow-1 { + flex-grow: 1; +} + +.container { + max-width: 600px; + margin: 0 auto; +} + +.hidden { + display: none; +} + +.mb-2 { + margin-bottom: 20px; +} + +/* Text Classes */ +.primary-text { + color: var(--primary-text); +} + +.secondary-text { + color: var(--secondary-text); +} + +.light-text { + color: var(--light-blue); +} + +.text-center { + text-align: center; +} + +.mb-2 { + margin-bottom: 20px; +} + +h1 { + font-size: 48px; + font-weight: 300; +} + +#appContainer { + height: 100vh; + width: 100vw; + align-items: stretch; + background-color: var(--primary-blue); + overflow: hidden; +} + +#mainContent { + width: 100%; + justify-content: center; +} + +/* Header */ +header { + height: 150px; + background-color: var(--dark-blue); + width: 100%; + color: white; +} + +header .container { + height: 100%; + align-items: left; +} + +/* Login */ + +#login { + background: white; + padding: 40px 100px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); + margin-top: -100px; +} + +#loginForm { + margin-top: 20px; +} + +/* Form Elements */ + +input { + height: 40px; + padding: 10px; + background-color: var(--light-blue); + border: none; + color: var(--secondary-text); +} + +button { + height: 40px; + padding: 0 15px; + background-color: var(--primary-blue); + color: white; + border: none; +} + +/* Chat */ + +#chat { + background: white; + height: 100px; + margin: 25px 0; + padding: 25px 25px; + color: white; + overflow-y: scroll; +} + +#messageForm { + margin-bottom: 25px; +} + +.message { + background-color: var(--primary-blue); + padding: 20px; + width: 80%; + margin-bottom: 20px; +} + +.message-right { + background: var(--light-blue); + color: var(--primary-text); + margin-left: 20%; +} + +.message-right .message-date { + color: var(--primary-text); +} + +.message-details { + margin-bottom: 5px; +} + +.message-author { + font-weight: 800; +} + +.message-date { + font-weight: 100; + font-size: 14px; +} + +.message-content { + font-weight: 100; +} diff --git a/part-7/public/app.js b/part-7/public/app.js new file mode 100644 index 0000000..ff6e213 --- /dev/null +++ b/part-7/public/app.js @@ -0,0 +1,84 @@ +const messageTypes = { LEFT: 'left', RIGHT: 'right', LOGIN: 'login' }; + +//Chat stuff +const chatWindow = document.getElementById('chat'); +const messagesList = document.getElementById('messagesList'); +const messageInput = document.getElementById('messageInput'); +const sendBtn = document.getElementById('sendBtn'); + +//login stuff +let username = ''; +const usernameInput = document.getElementById('usernameInput'); +const loginBtn = document.getElementById('loginBtn'); +const loginWindow = document.getElementById('login'); + +const messages = []; // { author, date, content, type } + +//Connect to socket.io - automatically tries to connect on same port app was served from +var socket = io(); + +createMessageHTML = message => { + if (message.type === messageTypes.LOGIN) { + return ` +

${ + message.author + } joined the chat...

+ `; + } + return ` +
+
+

${message.author}

+

${message.date}

+
+

${message.content}

+
+ `; +}; + +displayMessages = () => { + const messagesHTML = messages + .map(message => createMessageHTML(message)) + .join(''); + messagesList.innerHTML = messagesHTML; +}; + +sendBtn.addEventListener('click', e => { + e.preventDefault(); + if (!messageInput.value) { + return console.log('Invalid input'); + } + + const message = { + author: username, + date: new Date(), + content: messageInput.value + }; + + messages.push(message); + displayMessages(); + + //scroll to the bottom + chatWindow.scrollTop = chatWindow.scrollHeight; + + //clear input + messageInput.value = ''; +}); + +loginBtn.addEventListener('click', e => { + e.preventDefault(); + if (!usernameInput.value) { + return console.log('Must supply a username'); + } + + //set the username and create logged in message + username = usernameInput.value; + messages.push({ author: username, type: messageTypes.LOGIN }); + displayMessages(); + + //show chat window and hide login + loginWindow.classList.add('hidden'); + chatWindow.classList.remove('hidden'); +}); diff --git a/part-7/public/index.html b/part-7/public/index.html new file mode 100644 index 0000000..af3edc8 --- /dev/null +++ b/part-7/public/index.html @@ -0,0 +1,47 @@ + + + + + + + + Quick Chat + + + + + +
+
+
+

Quick Chat

+

The easiest way to get your message across.

+
+
+ +
+ +
+

Get Started

+
+ + +
+
+ + +
+
+ + + + + + \ No newline at end of file diff --git a/part-7/server.js b/part-7/server.js new file mode 100644 index 0000000..cad9e7b --- /dev/null +++ b/part-7/server.js @@ -0,0 +1,20 @@ +const express = require('express'); +const app = express(); +const http = require('http').Server(app); +const io = require('socket.io')(http); +const path = require('path'); + +//Serve public directory +app.use(express.static(path.join(__dirname, 'public'))); + +app.get('/', (req, res) => { + res.sendFile(path.join(__dirname, +'public/index.html')); +}); + +io.on('connection', socket => { + console.log('a user connected'); +}); + +http.listen(3000, () => { + console.log('listening on port 3000'); +}); diff --git a/part-8/package.json b/part-8/package.json new file mode 100644 index 0000000..074c4e8 --- /dev/null +++ b/part-8/package.json @@ -0,0 +1,15 @@ +{ + "name": "quick-chat", + "version": "1.0.0", + "description": "The easiest way to get your message across", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "James Q Quick", + "license": "ISC", + "dependencies": { + "express": "^4.16.3", + "socket.io": "^2.1.0" + } +} diff --git a/part-8/public/app.css b/part-8/public/app.css new file mode 100644 index 0000000..857763e --- /dev/null +++ b/part-8/public/app.css @@ -0,0 +1,182 @@ +/* Variabls */ +:root { + --primary-blue: #2196f3; + --dark-blue: #1976d2; + --light-blue: #bbdefb; + --white: #ffffff; + --primary-text: #212121; + --secondary-text: #757575; + --light-gray: #bdbdbd; +} + +/* Resets */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Helvetica, sans-serifs; + color: var(--primary-text); +} + +/* Helper Classes */ + +.flex { + display: flex; +} + +.flex-center { + justify-content: center; + align-items: center; +} + +.flex-column { + display: flex; + flex-direction: column; +} + +.flex-grow-1 { + flex-grow: 1; +} + +.container { + max-width: 600px; + margin: 0 auto; +} + +.primary-text { + color: var(--primary-text); +} + +.secondary-text { + color: var(--secondary-text); +} + +.light-text { + color: var(--light-blue); +} + +.hidden { + display: none; +} + +.text-center { + text-align: center; +} + +.mb-2 { + margin-bottom: 20px; +} + +h1 { + font-size: 48px; + font-weight: 300; +} + +#appContainer { + height: 100vh; + width: 100vw; + align-items: stretch; + background-color: var(--primary-blue); + overflow: hidden; +} + +#mainContent { + width: 100%; + justify-content: center; +} + +/* Header */ +header { + height: 150px; + background-color: var(--dark-blue); + width: 100%; + color: white; +} + +header .container { + height: 100%; + align-items: left; +} + +/* Login */ + +#login { + background: white; + padding: 40px 100px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); + margin-top: -100px; +} + +#loginForm { + margin-top: 20px; +} + +/* Form Elements */ + +input { + height: 40px; + padding: 10px; + background-color: var(--light-blue); + border: none; + color: var(--secondary-text); +} + +button { + height: 40px; + padding: 0 15px; + background-color: var(--primary-blue); + color: white; + border: none; +} + +/* Chat */ + +#chat { + background: white; + height: 100px; + margin: 25px 0; + padding: 25px 25px; + color: white; + overflow-y: scroll; +} + +#messageForm { + margin-bottom: 25px; +} + +.message { + background-color: var(--primary-blue); + padding: 20px; + width: 80%; + margin-bottom: 20px; +} + +.message-right { + background: var(--light-blue); + color: var(--primary-text); + margin-left: 20%; +} + +.message-right .message-date { + color: var(--primary-text); +} + +.message-details { + margin-bottom: 5px; +} + +.message-author { + font-weight: 800; +} + +.message-date { + font-weight: 100; + font-size: 14px; +} + +.message-content { + font-weight: 100; +} diff --git a/part-8/public/app.js b/part-8/public/app.js new file mode 100644 index 0000000..557f069 --- /dev/null +++ b/part-8/public/app.js @@ -0,0 +1,106 @@ +const messageTypes = { LEFT: 'left', RIGHT: 'right', LOGIN: 'login' }; + +//Chat stuff +const chatWindow = document.getElementById('chat'); +const messagesList = document.getElementById('messagesList'); +const messageInput = document.getElementById('messageInput'); +const sendBtn = document.getElementById('sendBtn'); + +//login stuff +let username = ''; +const usernameInput = document.getElementById('usernameInput'); +const loginBtn = document.getElementById('loginBtn'); +const loginWindow = document.getElementById('login'); + +const messages = []; // { author, date, content, type } + +//Connect to socket.io - automatically tries to connect on same port app was served from +var socket = io(); + +socket.on('message', message => { + //Update type of message based on username + if (message.type !== messageTypes.LOGIN) { + if (message.author === username) { + message.type = messageTypes.RIGHT; + } else { + message.type = messageTypes.LEFT; + } + } + + messages.push(message); + displayMessages(); + + //scroll to the bottom + chatWindow.scrollTop = chatWindow.scrollHeight; +}); + +createMessageHTML = message => { + if (message.type === messageTypes.LOGIN) { + return ` +

${ + message.author + } joined the chat...

+ `; + } + return ` +
+
+

${ + message.type === messageTypes.LEFT ? message.author : '' + }

+

${message.date}

+
+

${message.content}

+
+ `; +}; + +displayMessages = () => { + const messagesHTML = messages + .map(message => createMessageHTML(message)) + .join(''); + messagesList.innerHTML = messagesHTML; +}; + +sendBtn.addEventListener('click', e => { + e.preventDefault(); + if (!messageInput.value) { + return console.log('Invalid input'); + } + + const date = new Date(); + const month = ('0' + date.getMonth()).slice(0, 2); + const day = date.getDate(); + const year = date.getFullYear(); + const dateString = `${month}/${day}/${year}`; + + const message = { + author: username, + date: dateString, + content: messageInput.value + }; + sendMessage(message); + //clear input + messageInput.value = ''; +}); + +loginBtn.addEventListener('click', e => { + e.preventDefault(); + if (!usernameInput.value) { + return console.log('Must supply a username'); + } + + //set the username and create logged in message + username = usernameInput.value; + sendMessage({ author: username, type: messageTypes.LOGIN }); + + //show chat window and hide login + loginWindow.classList.add('hidden'); + chatWindow.classList.remove('hidden'); +}); + +sendMessage = message => { + socket.emit('message', message); +}; diff --git a/part-8/public/index.html b/part-8/public/index.html new file mode 100644 index 0000000..fcf28bf --- /dev/null +++ b/part-8/public/index.html @@ -0,0 +1,52 @@ + + + + + + + + Quick Chat + + + + + +
+ +
+
+

Quick Chat

+

The easiest way to get your message across.

+
+
+ +
+ +
+

Get Started

+
+ + +
+
+ + + + + +
+
+ + + + + + \ No newline at end of file diff --git a/part-8/server.js b/part-8/server.js new file mode 100644 index 0000000..b1829c7 --- /dev/null +++ b/part-8/server.js @@ -0,0 +1,30 @@ +const express = require('express'); +const app = express(); +const http = require('http').Server(app); +const io = require('socket.io')(http); +const path = require('path'); + +//Serve public directory +app.use(express.static(path.join(__dirname, 'public'))); + +app.get('/', (req, res) => { + res.sendFile(path.join(__dirname, +'public/index.html')); +}); + +io.on('connection', socket => { + console.log('a user connected'); + + socket.on('disconnect', () => { + console.log('user disconnected'); + }); + + socket.on('message', message => { + console.log('message: ' + message); + //Broadcast the message to everyone + io.emit('message', message); + }); +}); + +http.listen(3000, () => { + console.log('listening on port 3000'); +}); diff --git a/parts-3-and-4/app.css b/parts-3-and-4/app.css new file mode 100644 index 0000000..dbf4fc8 --- /dev/null +++ b/parts-3-and-4/app.css @@ -0,0 +1,187 @@ +/* Variabls */ +:root { + --primary-blue: #2196f3; + --dark-blue: #1976d2; + --light-blue: #bbdefb; + --white: #ffffff; + --primary-text: #212121; + --secondary-text: #757575; + --light-gray: #bdbdbd; +} + +/* Resets */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Helvetica, sans-serifs; + color: var(--primary-text); +} + +/* Helper Classes */ + +/* Layout/Flexbox */ +.flex { + display: flex; +} + +.flex-center { + justify-content: center; + align-items: center; +} + +.flex-column { + display: flex; + flex-direction: column; +} + +.flex-grow-1 { + flex-grow: 1; +} + +.container { + max-width: 600px; + margin: 0 auto; +} + +.hidden { + display: none; +} + +.mb-2 { + margin-bottom: 20px; +} + +/* Text Classes */ +.primary-text { + color: var(--primary-text); +} + +.secondary-text { + color: var(--secondary-text); +} + +.light-text { + color: var(--light-blue); +} + +.text-center { + text-align: center; +} + +.mb-2 { + margin-bottom: 20px; +} + +h1 { + font-size: 48px; + font-weight: 300; +} + +#appContainer { + height: 100vh; + width: 100vw; + align-items: stretch; + background-color: var(--primary-blue); + overflow: hidden; +} + +#mainContent { + width: 100%; + justify-content: center; +} + +/* Header */ +header { + height: 150px; + background-color: var(--dark-blue); + width: 100%; + color: white; +} + +header .container { + height: 100%; + align-items: left; +} + +/* Login */ + +#login { + background: white; + padding: 40px 100px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); + margin-top: -100px; +} + +#loginForm { + margin-top: 20px; +} + +/* Form Elements */ + +input { + height: 40px; + padding: 10px; + background-color: var(--light-blue); + border: none; + color: var(--secondary-text); +} + +button { + height: 40px; + padding: 0 15px; + background-color: var(--primary-blue); + color: white; + border: none; +} + +/* Chat */ + +#chat { + background: white; + margin: 25px 0; + padding: 25px 25px; + color: white; + overflow-y: scroll; +} + +#messageForm { + margin-bottom: 25px; +} + +.message { + background-color: var(--primary-blue); + padding: 20px; + width: 80%; + margin-bottom: 20px; +} + +.message-right { + background: var(--light-blue); + color: var(--primary-text); + margin-left: 20%; +} + +.message-right .message-date { + color: var(--primary-text); +} + +.message-details { + margin-bottom: 5px; +} + +.message-author { + font-weight: 800; +} + +.message-date { + font-weight: 100; + font-size: 14px; +} + +.message-content { + font-weight: 100; +} diff --git a/parts-3-and-4/index.html b/parts-3-and-4/index.html new file mode 100644 index 0000000..573db6e --- /dev/null +++ b/parts-3-and-4/index.html @@ -0,0 +1,70 @@ + + + + + + + + Quick Chat + + + + + +
+
+
+

Quick Chat

+

The easiest way to get your message across.

+
+
+ +
+ +
+

Get Started

+
+ + +
+
+ + +
+
+ + + + \ No newline at end of file diff --git a/parts-5-and-6/app.css b/parts-5-and-6/app.css new file mode 100644 index 0000000..c3bf895 --- /dev/null +++ b/parts-5-and-6/app.css @@ -0,0 +1,188 @@ +/* Variabls */ +:root { + --primary-blue: #2196f3; + --dark-blue: #1976d2; + --light-blue: #bbdefb; + --white: #ffffff; + --primary-text: #212121; + --secondary-text: #757575; + --light-gray: #bdbdbd; +} + +/* Resets */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Helvetica, sans-serifs; + color: var(--primary-text); +} + +/* Helper Classes */ + +/* Layout/Flexbox */ +.flex { + display: flex; +} + +.flex-center { + justify-content: center; + align-items: center; +} + +.flex-column { + display: flex; + flex-direction: column; +} + +.flex-grow-1 { + flex-grow: 1; +} + +.container { + max-width: 600px; + margin: 0 auto; +} + +.hidden { + display: none; +} + +.mb-2 { + margin-bottom: 20px; +} + +/* Text Classes */ +.primary-text { + color: var(--primary-text); +} + +.secondary-text { + color: var(--secondary-text); +} + +.light-text { + color: var(--light-blue); +} + +.text-center { + text-align: center; +} + +.mb-2 { + margin-bottom: 20px; +} + +h1 { + font-size: 48px; + font-weight: 300; +} + +#appContainer { + height: 100vh; + width: 100vw; + align-items: stretch; + background-color: var(--primary-blue); + overflow: hidden; +} + +#mainContent { + width: 100%; + justify-content: center; +} + +/* Header */ +header { + height: 150px; + background-color: var(--dark-blue); + width: 100%; + color: white; +} + +header .container { + height: 100%; + align-items: left; +} + +/* Login */ + +#login { + background: white; + padding: 40px 100px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); + margin-top: -100px; +} + +#loginForm { + margin-top: 20px; +} + +/* Form Elements */ + +input { + height: 40px; + padding: 10px; + background-color: var(--light-blue); + border: none; + color: var(--secondary-text); +} + +button { + height: 40px; + padding: 0 15px; + background-color: var(--primary-blue); + color: white; + border: none; +} + +/* Chat */ + +#chat { + background: white; + height: 100px; + margin: 25px 0; + padding: 25px 25px; + color: white; + overflow-y: scroll; +} + +#messageForm { + margin-bottom: 25px; +} + +.message { + background-color: var(--primary-blue); + padding: 20px; + width: 80%; + margin-bottom: 20px; +} + +.message-right { + background: var(--light-blue); + color: var(--primary-text); + margin-left: 20%; +} + +.message-right .message-date { + color: var(--primary-text); +} + +.message-details { + margin-bottom: 5px; +} + +.message-author { + font-weight: 800; +} + +.message-date { + font-weight: 100; + font-size: 14px; +} + +.message-content { + font-weight: 100; +} diff --git a/parts-5-and-6/app.js b/parts-5-and-6/app.js new file mode 100644 index 0000000..fb4e8b5 --- /dev/null +++ b/parts-5-and-6/app.js @@ -0,0 +1,81 @@ +const messageTypes = { LEFT: 'left', RIGHT: 'right', LOGIN: 'login' }; + +//Chat stuff +const chatWindow = document.getElementById('chat'); +const messagesList = document.getElementById('messagesList'); +const messageInput = document.getElementById('messageInput'); +const sendBtn = document.getElementById('sendBtn'); + +//login stuff +let username = ''; +const usernameInput = document.getElementById('usernameInput'); +const loginBtn = document.getElementById('loginBtn'); +const loginWindow = document.getElementById('login'); + +const messages = []; // { author, date, content, type } + +createMessageHTML = message => { + if (message.type === messageTypes.LOGIN) { + return ` +

${ + message.author + } joined the chat...

+ `; + } + return ` +
+
+

${message.author}

+

${message.date}

+
+

${message.content}

+
+ `; +}; + +displayMessages = () => { + const messagesHTML = messages + .map(message => createMessageHTML(message)) + .join(''); + messagesList.innerHTML = messagesHTML; +}; + +sendBtn.addEventListener('click', e => { + e.preventDefault(); + if (!messageInput.value) { + return console.log('Invalid input'); + } + + const message = { + author: username, + date: new Date(), + content: messageInput.value + }; + + messages.push(message); + displayMessages(); + + //scroll to the bottom + chatWindow.scrollTop = chatWindow.scrollHeight; + + //clear input + messageInput.value = ''; +}); + +loginBtn.addEventListener('click', e => { + e.preventDefault(); + if (!usernameInput.value) { + return console.log('Must supply a username'); + } + + //set the username and create logged in message + username = usernameInput.value; + messages.push({ author: username, type: messageTypes.LOGIN }); + displayMessages(); + + //show chat window and hide login + loginWindow.classList.add('hidden'); + chatWindow.classList.remove('hidden'); +}); diff --git a/parts-5-and-6/index.html b/parts-5-and-6/index.html new file mode 100644 index 0000000..f23ce9b --- /dev/null +++ b/parts-5-and-6/index.html @@ -0,0 +1,45 @@ + + + + + + + + Quick Chat + + + + + +
+
+
+

Quick Chat

+

The easiest way to get your message across.

+
+
+ +
+ +
+

Get Started

+
+ + +
+
+ + +
+
+ + + + \ No newline at end of file