docbrown/app/controllers/notifications_controller.rb
rhymes 50cdfb0a96 Do not cache articles notification time indication (#4122)
Fragment caching does not support an explicit expiration date for simplicitly, which means that cached resources are tied to the update datetime of the model.

In this case unfortunately this is not sufficient: a cached time that potentially never gets expired can stale quickly when applied to a time sensitive item like a notification.

Since the "time ago" computation is not particularly heavy and notifications are paginated, I ended up removing that part from the cache altogether.

As a byproduct I created two new separate cache keys for the article notification, one of each section surrounding the time.
2019-09-29 11:39:03 -04:00

79 lines
2.8 KiB
Ruby

class NotificationsController < ApplicationController
# No authorization required because we provide authentication on notifications page
def index
return unless user_signed_in?
@notifications_index = true
@user = user_to_view
@initial_page_size = 8
# NOTE: this controller is using offset based pagination by assuming that
# the id of the last notification also corresponds to the newest `notified_at`
# this might not be forever true but it's good enough for now
if params[:offset]
num = 30
notified_at_offset = Notification.find(params[:offset])&.notified_at
else
num = @initial_page_size
end
@notifications = if (params[:org_id].present? || params[:filter] == "org") && allowed_user?
organization_notifications
elsif params[:org_id].blank? && params[:filter].present?
filtered_notifications
else
@user.notifications
end
@notifications = @notifications.includes(:notifiable).without_past_aggregations.order(notified_at: :desc)
# if offset based pagination is invoked by the frontend code, we filter out all earlier ones
@notifications = @notifications.where("notified_at < ?", notified_at_offset) if notified_at_offset
@notifications = NotificationDecorator.decorate_collection(@notifications.limit(num))
@last_user_reaction = @user.reactions.last&.id
@last_user_comment = @user.comments.last&.id
@organizations = @user.member_organizations if @user.organizations
# The first call, the one coming from the browser URL bar will render the "index" view, which renders
# the first few notifications. After that the JS frontend code (see `initNotification.js`)
# will call this action again by sending the offset id for the last known notifications, the result
# will be the partial rendering of only the list of notifications that will be attached to the DOM by JS
render partial: "notifications_list" if notified_at_offset
end
private
def user_to_view
if params[:username] && current_user.admin?
User.find_by(username: params[:username])
else
current_user
end
end
def filtered_notifications
if params[:filter].to_s.casecmp("posts").zero?
@user.notifications.for_published_articles
elsif params[:filter].to_s.casecmp("comments").zero?
@user.notifications.for_comments.or(@user.notifications.for_mentions)
end
end
def organization_notifications
org_id = params[:org_id]
if params[:filter].to_s.casecmp("comments").zero?
Notification.for_organization_comments(org_id).or(Notification.for_organization_mentions(org_id))
else
Notification.for_organization(org_id)
end
end
def allowed_user?
@user.organization_id == params[:org_id] || @user.admin?
end
end