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/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]
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
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 (