diff --git a/app/controllers/api/v1/notifications_controller.rb b/app/controllers/api/v1/notifications_controller.rb index a277e91..0dd8698 100644 --- a/app/controllers/api/v1/notifications_controller.rb +++ b/app/controllers/api/v1/notifications_controller.rb @@ -11,6 +11,7 @@ def mark_as_read if params[:id] notification = current_user.notifications.find(params[:id]) notification.mark_as_read! + render json: notification, status: 200 else current_user.notifications.update_all(read_at: Time.now) end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 131e8b3..fe25e5c 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -17,7 +17,7 @@ def navbar_props }, userAttributes: { currentUserEmail: current_user&.email, - notificationCount: current_user&.unread_notifications_count + unreadNotificationCount: current_user&.unread_notifications_count }, isLoggedIn: current_user.present? } diff --git a/app/decorators/notification_decorator.rb b/app/decorators/notification_decorator.rb index 7fa4e49..d772138 100644 --- a/app/decorators/notification_decorator.rb +++ b/app/decorators/notification_decorator.rb @@ -1,6 +1,10 @@ class NotificationDecorator < ApplicationDecorator delegate_all + def decorated_created_at + "#{h.time_ago_in_words(object.created_at)} ago" + end + def mark_as_read_link unless object.read_at "| #{h.link_to 'mark as read', '#'}" diff --git a/app/javascript/components/NavBar.js b/app/javascript/components/NavBar.js index eff1649..89e74cd 100644 --- a/app/javascript/components/NavBar.js +++ b/app/javascript/components/NavBar.js @@ -12,15 +12,43 @@ class NavBar extends Component { } componentDidMount() { + console.log(this.state); if (this.state.isLoggedIn) { axios.get(this.state.routes.lastFiveNotificationsPath) .then((response) => { this.setState({ notifications: response.data }) + console.log(this.state); }) } else { } } + markSingleNotificationAsRead = (notificationId) => { + if (notificationId) { + const csrfToken = document.querySelector('[name="csrf-token"]').content; + axios.defaults.headers.common['X-CSRF-Token'] = csrfToken; + + axios.patch(`/api/v1/notifications/${notificationId}/mark_as_read`) + .then((response) => { + const newUnreadCount = this.state.userAttributes.unreadNotificationCount - 1 + const index = this.state.notifications.findIndex((notification) => notification.id === notificationId) + const updatedNotifications = this.state.notifications + updatedNotifications[index] = response.data + const newState = { ...this.state.userAttributes } + + newState.unreadNotificationCount = newUnreadCount + this.setState({ notifications: updatedNotifications, userAttributes: newState }) + console.log(this.state); + + // this is correct, i need to replace the notification in the notification part of state to refresh the dropdown + }) + .catch((error) => { + console.log(error); + }) + } + // make api call + } + render () { const { isLoggedIn, userAttributes, routes, notifications } = this.state const { rootPath } = routes @@ -35,7 +63,12 @@ class NavBar extends Component { { isLoggedIn - ? + ? : } diff --git a/app/javascript/components/Notification.js b/app/javascript/components/Notification.js index 374cedc..5d8c01d 100644 --- a/app/javascript/components/Notification.js +++ b/app/javascript/components/Notification.js @@ -1,9 +1,16 @@ import React from 'react' -const Notification = ({ notificationText }) => { +const Notification = ({ notificationText, notificationReadAt, notificationCreatedAt, notificationId, markSingleNotificationAsRead }) => { return (
{ notificationText } +
+ { notificationCreatedAt } + { + !notificationReadAt && + | markSingleNotificationAsRead(notificationId) } className="muted-text caption-text">mark as read + } +
) } diff --git a/app/javascript/components/UserNav.js b/app/javascript/components/UserNav.js index 54337bd..3fe4377 100644 --- a/app/javascript/components/UserNav.js +++ b/app/javascript/components/UserNav.js @@ -1,18 +1,25 @@ -import React, { Component } from 'react' + import React, { Component } from 'react' import Notification from './Notification' class UserNav extends Component { renderNotifications = (notes) => { return notes.map((note) => { - return + return }) } render() { - const { routes, userAttributes, notifications } = this.props + const { routes, userAttributes, notifications, markSingleNotificationAsRead } = this.props const { leaguesPath, dashboardPath, newLeaguePath, destroyUserSessionPath, notificationsPath } = routes - const { currentUserEmail, notificationCount } = userAttributes + const { currentUserEmail, unreadNotificationCount } = userAttributes return ( { /* user dropdown */ } -
  • +
  • { - notifications + notifications && notifications.length > 0 ? this.renderNotifications(notifications) - :
    nope
    + :
    There are currently no notifications!
    }
    view all notifications - mark all as read + { + unreadNotificationCount > 0 && mark all as read + }
  • { /* notifications dropdown */ } diff --git a/app/models/user.rb b/app/models/user.rb index f5bab7b..7c75793 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -21,7 +21,7 @@ def full_name end def last_five_notifications - notifications.last(5) + notifications.order(created_at: :desc).first(5) end def number_of_leagues_played_in diff --git a/app/serializers/notification_serializer.rb b/app/serializers/notification_serializer.rb index ab5c841..e60d10c 100644 --- a/app/serializers/notification_serializer.rb +++ b/app/serializers/notification_serializer.rb @@ -1,7 +1,16 @@ +require 'action_view' +require 'action_view/helpers' + class NotificationSerializer < ActiveModel::Serializer - attributes :id, :note_text + include ActionView::Helpers::DateHelper + + attributes :id, :note_text, :read_at, :decorated_created_at def note_text object.notification_text end + + def decorated_created_at + object.decorate.decorated_created_at + end end diff --git a/config/database.yml b/config/database.yml index a3b0689..937e020 100644 --- a/config/database.yml +++ b/config/database.yml @@ -20,6 +20,7 @@ default: &default # For details on connection pooling, see Rails configuration guide # http://guides.rubyonrails.org/configuring.html#database-pooling pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + host: localhost development: <<: *default diff --git a/spec/requests/api/v1/notifications_controller_spec.rb b/spec/requests/api/v1/notifications_controller_spec.rb index 14930df..774be38 100644 --- a/spec/requests/api/v1/notifications_controller_spec.rb +++ b/spec/requests/api/v1/notifications_controller_spec.rb @@ -9,10 +9,12 @@ describe 'as a user' do let(:expected_return) do - notifications.map do |note| + notifications.sort_by(&:created_at).reverse.map do |note| { "id" => note.id, - "note_text" => note.notification_text + "note_text" => note.notification_text, + "read_at" => note.read_at, + "decorated_created_at" => note.decorate.decorated_created_at } end end