From 2c6e61ec1896d0759002b738ea2af9532f154617 Mon Sep 17 00:00:00 2001 From: Kamarasinghe Date: Fri, 7 Sep 2018 17:10:58 -0700 Subject: [PATCH 1/3] Added comments to App.jsx --- Gemfile | 2 +- Gemfile.lock | 4 ++-- app/javascript/components/App.jsx | 35 ++++++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index fda6ec7..f9f403f 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } -ruby '2.3.1' +ruby '2.4.1' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 5.2.1' diff --git a/Gemfile.lock b/Gemfile.lock index 6eae351..2461d41 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -223,7 +223,7 @@ DEPENDENCIES webpacker RUBY VERSION - ruby 2.3.1p112 + ruby 2.4.1p111 BUNDLED WITH - 1.16.2 + 1.16.4 diff --git a/app/javascript/components/App.jsx b/app/javascript/components/App.jsx index c15a584..842d404 100644 --- a/app/javascript/components/App.jsx +++ b/app/javascript/components/App.jsx @@ -15,6 +15,7 @@ const mapStateToProps = (state) => { }; }; +// Used to const mapDispatchToProps = (dispatch) => { return { signIn: userInfo => dispatch(signIn(userInfo)), @@ -25,9 +26,12 @@ const mapDispatchToProps = (dispatch) => { export class App extends Component { constructor() { super(); + // These are all stored in state so that + // signIn and signUp forms can update them from + // their individual component this.state = { signup: false, - signin: false, + signin: false, admin: false, first: '', email: '', @@ -42,6 +46,8 @@ export class App extends Component { this.handleChange = this.handleChange.bind(this); } + // Grabs the required information to send with post + // request to save information to database signUp() { axios.post('/signup', { user: { @@ -61,14 +67,24 @@ export class App extends Component { }); } + // Send the login information via post request signIn() { axios.post('/login', { username: this.state.username, password: this.state.password }).then((res) => { + // If the response is 'failed', then + // alert the user. I chose to user alert + // so that the user is aware that they + // were not able to log in if (res.data === 'failed') { alert('Invalid username or password'); } else { + // If successfully signed in, I need to + // set the state with first name for the + // nav bar greeting and set the admin, + // username, and password back to a + // neutral state this.setState({ admin: false, first: res.data.first, @@ -76,12 +92,16 @@ export class App extends Component { password: '' }); + // Send username, isAdmin, and userId information to the redux store + // and then close the signIn modal this.props.signIn({ username: res.data.username, isAdmin: res.data.isAdmin, userId: res.data.id }); this.toggleModal('signin'); } }); } + // Send a delete request that destroys the user session + // and set firstName to neutral state signOut() { axios.delete('/logout') .then((res) => { @@ -89,10 +109,14 @@ export class App extends Component { first: '', }); + // Call the redux action to sign user out this.props.signOut(); }); } + // If the event name is 'admin' set state true or + // false for the signup toggle, else update the + // corresponding state name with value handleChange(event) { if (event.target.name === 'admin') { this.setState({ @@ -118,6 +142,8 @@ export class App extends Component { ) } + // If there is no user signed in, show the sign in and sign up buttons + // If there is a user signed in, then greet them with their first name render() { return (
@@ -153,3 +179,10 @@ export class App extends Component { }; export default connect(mapStateToProps, mapDispatchToProps)(App); + +/* + I chose to use modals here because I believe it makes for a better user experience + if the user isn't redirected from page to page. The state stored in this component are + all changed according to user input whereas the Redux store is used to set persistant + data such as username and if their admin. +*/ \ No newline at end of file From 677e38de4226c49d6ea5df43920adbecbb14da1c Mon Sep 17 00:00:00 2001 From: Kamarasinghe Date: Sat, 8 Sep 2018 12:08:02 -0700 Subject: [PATCH 2/3] Added comments to Messages component --- app/javascript/components/Messages.jsx | 45 ++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/app/javascript/components/Messages.jsx b/app/javascript/components/Messages.jsx index 3a37fbc..831e8d3 100644 --- a/app/javascript/components/Messages.jsx +++ b/app/javascript/components/Messages.jsx @@ -57,10 +57,18 @@ export class Messages extends Component { this.paginationFilter = this.paginationFilter.bind(this); } + // Invoke the function that gets the messages + // from the database when the component mounts componentDidMount() { this.getMessages(); } + // Makes the getRequest to /messages and sets + // state of paginationNum(the maximum amount + // of pages), sends the messages to the store + // for easy access in other components, and also + // sets the first 5 messages to show using the + // paginationFilter function getMessages() { axios.get('/messages') .then((res) => { @@ -83,6 +91,16 @@ export class Messages extends Component { }); } + // Verifies that the messages is at least 4 + // characters. If successfully submitted, then + // runs getMessages function to retrieve newest + // message, changes the page back to where user + // was previously and deletes the user input + // from the messageInput box. If not successful + // shows user error message to make sure they are + // logged in which is the likely cause the message + // did not get saved to database. Alerts the user + // if the message is not at least 4 characters. messageSubmit() { if (this.state.message.length >= 4) { axios.post('/message', { messages: { message: this.state.message, user_id: this.props.userId }}) @@ -100,6 +118,8 @@ export class Messages extends Component { } } + // Handles the deleting of a message by sending + // the message id with the delete request to /message messageDelete(id) { axios.delete('/message', { data: { messageId: id }}) .then(() => { @@ -107,6 +127,11 @@ export class Messages extends Component { }); } + // Updates a previously inputted message using a + // patch request to /message. Sends message id + // and the new message along with the request. In + // order to submit, the message must be at least + // 4 characters. messageUpdate(id) { if (this.state.message.length >= 4) { axios.patch('/message', { messageId: id, newMessage: this.state.message }) @@ -119,6 +144,9 @@ export class Messages extends Component { } } + // If a message is sent via argument: opens the modal to edit message, + // sets the selected message in state for access in Selected Message + // component. If no message selected, closes the modal. selectMessage(message) { if (message) { this.setState({ @@ -133,6 +161,10 @@ export class Messages extends Component { } } + // compareFunction passed into the sort method + // Assigns value, 0, 1, or -1, when comparing + // two properties. If descending order, then + // multiply the 'score' by -1 compareValues(key, createdAt, order='asc') { return function(a, b) { // Grab the user property within message @@ -164,6 +196,11 @@ export class Messages extends Component { }; } + // Takes in an array that has which category to filter + // and which direction to sort in. Invoke the sort method + // with the compareValues function to sort the messages. Set + // allMessages to the newly sorted and run the paginationFilter + // with the first page passed in. filter(action) { let category = action[0]; let createdAt = category === 'created_at' ? false : true; @@ -213,6 +250,8 @@ export class Messages extends Component { } } + // Sets the currentMessages state which are the + // messages that are going to be displayed paginationFilter(pageNum) { // Designate how many messages to show let endSlice = pageNum * 5; @@ -225,6 +264,12 @@ export class Messages extends Component { }); } + // Map through the current messages and display the first name, username, and the message + // Incorporate conditional rendering so that the current user can edit/delete their own message + // or if they are admin, can edit/delete any users message + // Create an array the length of the paginationNum and map through to create each individual + // pagination number + // Conditional renders the SelectedMessage component depending on the value of selectMessage render() { return (
From 59c8a43a69fd341928eae70acfe20ee5a4ba27df Mon Sep 17 00:00:00 2001 From: Kamarasinghe Date: Sat, 8 Sep 2018 14:50:57 -0700 Subject: [PATCH 3/3] Commented on require_user method and skip_before_action --- app/controllers/application_controller.rb | 9 ++++----- app/controllers/message_controller.rb | 4 ++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index ea94c3d..fad1fb1 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,15 +1,14 @@ class ApplicationController < ActionController::Base helper_method :current_user + + # Unless there is a user logged in, they would always be redirected + # to the root_url if they try to access another RESTful endpoint def current_user @current_user ||= User.find_by(id: session[:user_id]) if session[:user_id] end - def is_admin - @current_user ||= User.find(session[:is_admin]) if session[:is_admin] - end - def require_user - redirect_to 'root_url' unless is_admin || current_user + redirect_to 'root_url' unless current_user end end diff --git a/app/controllers/message_controller.rb b/app/controllers/message_controller.rb index c4ceecd..8ddae19 100644 --- a/app/controllers/message_controller.rb +++ b/app/controllers/message_controller.rb @@ -1,4 +1,8 @@ class MessageController < ApplicationController + # Skip the action of verifying the token authenticity + # and require a user to be logged in prior to any + # action besides the index action which loads the messages + skip_before_action :verify_authenticity_token before_action :require_user, except: [:index]