Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Luke's Frontend Chitter #117

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
bundle.js
coverage
34 changes: 10 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,23 @@
# Chitter API Frontend Challenge
# Chitter API Challenge

* Feel free to use Google, your notes, books, etc. but work on your own
* If you refer to the solution of another coach or student, please put a link to that in your README
* If you have a partial solution, **still check in a partial solution**
* You must submit a pull request to this repo with your code by 9am Monday morning
This was a challenge to build a Twitter clone that connected to a backend REST API. Users were able to sing up, log in and then post messages to a public stream. Having completed the challenge I have a much better understanding of how to write and test asynchoronous code in Javascript, as well as use of fetch and the most common RESTful methods like POST, GET, PATCH and DELETE.

Challenge:
-------
Language - Javscript
Testing - Jest

As usual please start by forking this repo.

We are going to write a small Twitter clone that will allow the users to post messages to a public stream.

The scenario is similar to the [Chitter Challenge](https://github.com/makersacademy/chitter-challenge), except someone has already built a backend API for you and hosted it on Heroku.

Your task is to build a front-end single-page-app to interface with this API. You can do this in any framework you like, or in pure Javascript. [The API documentation is here.](https://github.com/makersacademy/chitter_api_backend)

Here are some interactions the API supports. Implement as many as you see fit.
## Requirements

* Creating Users
* Logging in
* Posting Peeps
* Viewing all Peeps *(I suggest you start here)*
* Viewing all Peeps
* Viewing individual Peeps
* Deleting Peeps
* Liking Peeps
* Unliking Peeps

We are looking for well tested, easy to read, easy to change code. This is more important than the number of interactions you implement.

Note that others may be doing the same task at the same time, so the data may change as you are using it.

## Utilities you might find useful
### Screenshots

* [The Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) for making requests.
* [Postman](https://www.getpostman.com/) or [Insomnia](https://insomnia.rest/) for exploring the API.
<img width="1369" alt="Screenshot 2022-07-03 at 16 55 13" src="https://user-images.githubusercontent.com/95092176/177047493-fbb809a1-525f-4651-a796-b4f189444f0c.png">
<img width="1290" alt="Screenshot 2022-07-03 at 16 55 53" src="https://user-images.githubusercontent.com/95092176/177047494-ec31a312-7f6f-44ec-ac2b-a832ae6b569a.png">
<img width="1355" alt="Screenshot 2022-07-03 at 16 56 03" src="https://user-images.githubusercontent.com/95092176/177047496-83ccb00a-4d66-4eb3-a9b7-7d3f509efece.png">
64 changes: 64 additions & 0 deletions chitterApi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
class ChitterApi {
async fetchPeeps() {
const response = await fetch(
"https://chitter-backend-api-v2.herokuapp.com/peeps"
);

const peeps = await response.json();
return peeps;
}

async createUser(handle, password) {
const response = await fetch(
"https://chitter-backend-api-v2.herokuapp.com/users",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
user: { handle: handle, password: password },
}),
}
);
const user = await response.json();
return user;
}

async logInUser(handle, password) {
const response = await fetch(
"https://chitter-backend-api-v2.herokuapp.com/sessions",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
session: { handle: handle, password: password },
}),
}
);
const confirmation = await response.json();
return confirmation;
}

async createPeep(session_key, user_id, body) {
const response = await fetch(
"https://chitter-backend-api-v2.herokuapp.com/peeps",
{
method: "POST",
headers: {
Authorization: `Token token=${session_key}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
peep: { user_id: user_id, body: body },
}),
}
);
const peep = await response.json();
return peep;
}
}

module.exports = ChitterApi;
37 changes: 37 additions & 0 deletions chitterModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
class ChitterModel {
constructor() {
this.peeps = [];
this.session = null;
}

getSession() {
return this.session;
}

saveSession(session) {
this.session = session;
}

getPeeps() {
return this.peeps;
}

addPeep(peep) {
this.peeps.push(peep);
}

addNewPeep(peep) {
this.peeps.unshift(peep);
}

setPeeps(peeps) {
this.reset();
peeps.forEach((peep) => this.addPeep(peep));
}

reset() {
this.peeps = [];
}
}

module.exports = ChitterModel;
132 changes: 132 additions & 0 deletions chitterView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
class ChitterView {
constructor(model, api) {
this.model = model;
this.api = api;

this.peepList = document.querySelector("#peep-list");
this.successAlert = document.querySelector("#success-alert");
this.failAlert = document.querySelector("#fail-alert");

this.createAccountBtn = document.querySelector("#user-create-btn");
this.createHandleInput = document.querySelector("#create-account-handle");
this.createPasswordInput = document.querySelector(
"#create-account-password"
);
this.createAccountSubmit = document.querySelector("#create-account-submit");

this.createAccountSubmit.addEventListener("click", () => {
this.createAccount(
this.createHandleInput.value,
this.createPasswordInput.value
);
});

this.loginBtn = document.querySelector("#user-login-btn");
this.loginHandleInput = document.querySelector("#login-handle");
this.loginPasswordInput = document.querySelector("#login-password");
this.loginSubmit = document.querySelector("#login-submit");
this.loginSubmit.addEventListener("click", () => {
this.signIn(this.loginHandleInput.value, this.loginPasswordInput.value);
});

this.peepInput = document.querySelector("#peep-input");
this.peepSubmit = document.querySelector("#peep-submit");
this.peepSubmit.addEventListener("click", () => {
this.newPeep(
this.model.getSession().session_key,
this.model.getSession().user_id,
this.peepInput.value
);
});
}

async displayPeepsFromApi() {
const peeps = await this.api.fetchPeeps();
this.model.setPeeps(peeps);
this.displayPeeps();
}

displayPeeps() {
this.clearPeepDivs();
let peeps = this.model.getPeeps();

peeps.forEach((peep) => {
this.addPeepLi(peep);
});
}

clearPeepDivs() {
const peeps = document.querySelectorAll("li.peep");
peeps.forEach((peep) => {
peep.remove();
});
}

async createAccount(handle, password) {
if (handle && password) {
const user = await this.api.createUser(handle, password);

// this.showAndHide("#success-alert");
// this.successAlert.innerText = `Thanks ${user.handle} your account has been created`;

this.createHandleInput.value = null;
this.createPasswordInput.value = null;

this.signIn(handle, password);
}
}

async signIn(handle, password) {
if (handle && password) {
const response = await this.api.logInUser(handle, password);

this.model.saveSession(response);

this.showAndHide("#success-alert");
this.successAlert.innerText = `Thanks ${response.session_key} you have successfully logged in`;

this.loginHandleInput.value = null;
this.loginPasswordInput.value = null;

this.showAndHide("#user-container");

this.showAndHide("#peep-container");
}
}

async newPeep(session_key, user_id, body) {
const peep = await this.api.createPeep(session_key, user_id, body);

this.model.addNewPeep(peep);
this.displayPeeps();

this.peepInput.value = null;
}

addPeepLi(peep) {
const time = this.formatTime(peep);

this.peepList.innerHTML += `<li class="list-group-item peep">
<h5 class="fw-bold">${peep.user.handle}</h5>
<p class="text-muted mb-2 fw-bold">${time}</p>
<p class="text-muted mb-0 peep">${peep.body}</p>
</li>`;
}

formatTime(peep) {
let time = new Date(peep.created_at);

return time.toDateString();
}

showAndHide(element) {
var x = document.querySelector(element);
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}
}

module.exports = ChitterView;
23 changes: 23 additions & 0 deletions css/mdb.dark.min.css

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions css/mdb.dark.min.css.map

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions css/mdb.dark.rtl.min.css

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions css/mdb.dark.rtl.min.css.map

Large diffs are not rendered by default.

40 changes: 40 additions & 0 deletions css/mdb.min.css

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions css/mdb.min.css.map

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions css/mdb.rtl.min.css

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions css/mdb.rtl.min.css.map

Large diffs are not rendered by default.

Loading