diff --git a/README.md b/README.md index fe82c877725..cd3b9e8152c 100644 --- a/README.md +++ b/README.md @@ -16,3 +16,19 @@ This repository is the starter code for the project: Students will fork and clon - Express - Node 5.10.x or above + +## Final Product + +- Main page that displays user, a tweet form and past tweets +!["Main"](docs/main.png) + +- User can create new tweet of upto 140 characters. +!["New-Tweet"](docs/new-tweet.png) + +- Displays error if user does not enter text or input>140 characters +!["Error"](docs/error.png) + +- Provides view on mobile device. +!["Mobile"](docs/mobile.png) + + diff --git a/docs/error.png b/docs/error.png new file mode 100644 index 00000000000..6b62f56d1ed Binary files /dev/null and b/docs/error.png differ diff --git a/docs/main.png b/docs/main.png new file mode 100644 index 00000000000..67b7cead682 Binary files /dev/null and b/docs/main.png differ diff --git a/docs/mobile.png b/docs/mobile.png new file mode 100644 index 00000000000..83558e8e429 Binary files /dev/null and b/docs/mobile.png differ diff --git a/docs/new-tweet.png b/docs/new-tweet.png new file mode 100644 index 00000000000..302d6f488cb Binary files /dev/null and b/docs/new-tweet.png differ diff --git a/package-lock.json b/package-lock.json index 8177739a4db..5761745aaed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -854,7 +854,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -875,12 +876,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -895,17 +898,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -1022,7 +1028,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -1034,6 +1041,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -1048,6 +1056,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1055,12 +1064,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -1079,6 +1090,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -1159,7 +1171,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -1171,6 +1184,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -1256,7 +1270,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -1292,6 +1307,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -1311,6 +1327,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -1354,12 +1371,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/package.json b/package.json index d15d1347798..40ad1ff34ca 100644 --- a/package.json +++ b/package.json @@ -1,22 +1,22 @@ { - "name": "tweeter", - "version": "1.0.0", - "description": "A twitter clone by Lighthouse Labs for web bootcamp students to learn front-end dev skillz", - "main": "server/index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "node server/index.js", - "local": "./node_modules/.bin/nodemon --watch server -e js server/index.js" - }, - "author": "Lighthouse Labs", - "license": "ISC", - "dependencies": { - "body-parser": "^1.15.2", - "chance": "^1.0.2", - "express": "^4.13.4", - "md5": "^2.1.0" - }, - "devDependencies": { - "nodemon": "^1.9.2" - } -} + "name": "tweeter", + "version": "1.0.0", + "description": "A twitter clone by Lighthouse Labs for web bootcamp students to learn front-end dev skillz", + "main": "server/index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server/index.js", + "local": "./node_modules/.bin/nodemon --watch server -e js server/index.js" + }, + "author": "Lighthouse Labs", + "license": "ISC", + "dependencies": { + "body-parser": "^1.15.2", + "chance": "^1.0.2", + "express": "^4.13.4", + "md5": "^2.1.0" + }, + "devDependencies": { + "nodemon": "^1.9.2" + } +} \ No newline at end of file diff --git a/public/index.html b/public/index.html index 3eff8a8578d..b70f902eaf7 100644 --- a/public/index.html +++ b/public/index.html @@ -1,29 +1,97 @@ - + + Tweeter - Home Page + + + + + + + + - + - - - + + + + + + + + + + +

+
+
+ + +

Baani D

+ +
+ + + +
+
+ +
+
+ +
+ + 140 +
+
+
+
+

+
+ +
+ + Andy + @Newton +
+
+

Simplicity is the ultimate sophistication.

+
+ +
+
+
+ +
+ + - -
- - + \ No newline at end of file diff --git a/public/scripts/client.js b/public/scripts/client.js index 80ad84592c5..7f5d91c4ddb 100644 --- a/public/scripts/client.js +++ b/public/scripts/client.js @@ -1,6 +1,109 @@ -/* - * Client-side JS logic goes here - * jQuery is already loaded - * Reminder: Use (and do all your DOM work in) jQuery's document ready function - */ +//js code for submitting and loading tweets from a given url +const createTweetElement = function(tweetData) { + //when tweet was created (10 days ago) + const time = timeago.format(tweetData.created_at); + //escaping text input for any script tag in tweet + const safeHTML = `${escape(tweetData.content.text)}`; + + //render tweets data + const $tweet = $(`
+ +
+ + ${tweetData.user.name} + ${tweetData.user.handle} +
+
+

${safeHTML}

+
+ +
`); + + return $tweet; + } + //creating a new tweet and adding to twwets container +const renderTweets = function(tweets) { + // remove tweets from tweets container before adding a new tweet + $('.tweetContainer').empty(); + + for (user of tweets) { + let $tweetData = createTweetElement(user); + $('.tweetContainer').prepend($tweetData); + } + +} + +//load tweets from specified url +const loadTweets = function() { + const url = "/tweets"; + $.ajax({ + url: url, + method: 'GET', + + }) + .then((results) => { + renderTweets(results); + }) + } + //escaping any malicious content from tweet input + //like +const escape = function(str) { + let div = document.createElement("div"); + div.appendChild(document.createTextNode(str)); + return div.innerHTML; +}; + +$(document).ready(function() { + loadTweets(); + + //eventlistener for submit(tweet) button + $("#tweetform").submit(function(event) { + + // prevent from submitting a form + event.preventDefault(); + + //create a URL encoded text string for transmitting + // like hello%20user + const str = $(this).serialize(); + + //length of input text in tweet text area + const currentLength = $('#textarea').val().length; + + //error message initially hidden + $("#error").hide(); + + //show error messgae if input text length>140 + if (currentLength > 140) { + $("#error").show().html(" Characters limit Exceeded"); + } + + //show error messgae if no input + if (currentLength === 0) { + $("#error").show().html("No Input"); + } + + //ajax request for submitting and loading tweets + const url = "/tweets"; + $.ajax({ + url: url, + method: 'POST', + data: str + + }) + .then((results) => { + loadTweets(); + + //clear text area once tweet submitted + $('#textarea').val(''); + $('#counter').val(140); + }) + }); +}); \ No newline at end of file diff --git a/public/scripts/composer-char-counter.js b/public/scripts/composer-char-counter.js new file mode 100644 index 00000000000..d5a0165a2d7 --- /dev/null +++ b/public/scripts/composer-char-counter.js @@ -0,0 +1,18 @@ +$(document).ready(function() { + $('#textarea').keyup(updateCount); + // update counter for character input + function updateCount() { + + //find length of textarea input + const count = $(this).val().length; + const allowed = 140 - count; + + //add or remove warning based on characetr count + if (allowed > 0) { + $("#counter").removeClass('warning').text(allowed); + } else { + $("#counter").addClass('warning').text(allowed); + } + } + +}); \ No newline at end of file diff --git a/public/styles/header.css b/public/styles/header.css new file mode 100644 index 00000000000..b6b29784795 --- /dev/null +++ b/public/styles/header.css @@ -0,0 +1,10 @@ +.header { + display: flex; + flex-direction: column; + height: 400px; + margin-top: 100px; + align-items: center; + justify-content: center; + background-color: burlywood; + margin-right: 50px; +} \ No newline at end of file diff --git a/public/styles/layout.css b/public/styles/layout.css index 7ecca6f8137..6bea52775cd 100644 --- a/public/styles/layout.css +++ b/public/styles/layout.css @@ -3,23 +3,71 @@ * https://css-tricks.com/international-box-sizing-awareness-day/ * Do not modify these two selectors, please */ + html { - box-sizing: border-box; + box-sizing: border-box; } -*, *:before, *:after { - box-sizing: inherit; + +*, +*:before, +*:after { + box-sizing: inherit; } + /* * Global layout stuff */ body { - color: #545149; - background-color: #f4f1ec; - font-size: 24px; + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + color: #545149; + background-color: #f4f1ec; + font-size: 24px; + font-family: 'Source Sans Pro', sans-serif; + background-color: papayawhip; } +.header-tweet-container { + display: flex; + justify-content: space-around; + width: 60%; +} + + /* * Additional CSS can be defined below or in other CSS files, as per instructions - */ \ No newline at end of file + */ + +.form-tweet-container { + display: flex; + flex-direction: column; + justify-content: center; + text-align: center; + background-color: papayawhip; + margin-top: 200px; +} + + +/* responsive design for devices*/ + +@media only screen and (max-width: 768px) { + .header-tweet-container { + flex-direction: column; + width: 100%; + } + .header { + margin-top: 50px; + width: 100% + } + .form-tweet-container { + width: 90%; + margin-top: 50px; + } + nav { + background-color: burlywood; + } +} \ No newline at end of file diff --git a/public/styles/nav.css b/public/styles/nav.css new file mode 100644 index 00000000000..702e74ac38a --- /dev/null +++ b/public/styles/nav.css @@ -0,0 +1,20 @@ +nav { + display: flex; + justify-content: space-between; + padding: 1.5em; + height: 120px; + background-color: #4056A1; + color: #ffffff; + position: fixed; + width: 100%; + z-index: 10; + top: 0; +} + +.tweeter { + font-family: bungee; +} + +.new { + text-align: center; +} \ No newline at end of file diff --git a/public/styles/new-tweet.css b/public/styles/new-tweet.css new file mode 100644 index 00000000000..f0bb6abe47c --- /dev/null +++ b/public/styles/new-tweet.css @@ -0,0 +1,110 @@ +.new-tweet { + width: 100%; + align-items: center; + display: flex; + justify-content: space-between; + flex-direction: column; + background-color: papayawhip; +} + +#textarea { + display: flex; + width: 100%; + border: none; + border-bottom: black 2px solid; + background: none; + text-align: left; + background-color: papayawhip; + margin-bottom: 0.75em; + outline: none; +} + +#counter, +#icons, +#at-rate { + float: right; +} + +#submit { + float: left; + background-color: #4056A1; + color: #FFFFFF; + font-family: Impact, Haettenschweiler, 'Arial Bold', sans-serif; +} + +.new-tweet form { + width: 100%; +} + + +/* warning class for counter */ + +form .warning { + color: red; +} + +#error { + color: #D8000C; + background-color: #FFD2D2; + border: 1 px solid red; +} + + +/* tweet data */ + +.tweetContainer { + display: flex; + flex-direction: column; + background: none; + text-align: left; + margin-top: 10px; + background-color: papayawhip; +} + + +/* article class for displaying tweets */ + +.art-tweet { + width: 100%; + margin: 10px; + border: 2px solid; +} + +.art-tweet:hover { + border: 2px solid; + padding: 10px; + box-shadow: 5px 10px #888888; +} + +.art-tweet footer { + border-top: 2px solid black; +} + + +/* twitter handle */ + +#at-rate { + color: gray; +} + + +/* icons */ + +.fa-flag, +.fa-retweet, +.fa-heart { + color: blue; +} + +.fa-retweet:hover, +.fa-heart:hover, +.fa-flag:hover { + color: yellow; +} + + +/* icon for writing new tweet */ + +.fa-angle-double-down { + color: red; +} \ No newline at end of file diff --git a/server/data-files/initial-tweets.json b/server/data-files/initial-tweets.json index 2b03138f70b..902e1755106 100644 --- a/server/data-files/initial-tweets.json +++ b/server/data-files/initial-tweets.json @@ -2,23 +2,23 @@ { "user": { "name": "Newton", - "avatars": "https://i.imgur.com/73hZDYK.png" - , + "avatars": "https://i.imgur.com/73hZDYK.png", "handle": "@SirIsaac" }, "content": { "text": "If I have seen further it is by standing on the shoulders of giants" }, - "created_at": 1461116232227 + "created_at": 1637110328563 }, { "user": { "name": "Descartes", "avatars": "https://i.imgur.com/nlhLi3I.png", - "handle": "@rd" }, + "handle": "@rd" + }, "content": { "text": "Je pense , donc je suis" }, - "created_at": 1461113959088 + "created_at": 1637196728563 } -] +] \ No newline at end of file