From 1ec5ffa6d23568490be6c636f8905c497f0b7ff4 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Sat, 25 Feb 2023 15:46:11 +0100 Subject: [PATCH] Cache inbox and notification counters --- app/helpers/application_helper.rb | 24 ------------------- app/models/inbox.rb | 8 ++++--- app/models/notification.rb | 2 +- app/models/user.rb | 1 + app/models/user/inbox_methods.rb | 17 +++++++++++++ app/models/user/notification_methods.rb | 14 +++++++++++ app/views/navigation/_main.html.haml | 6 +++-- ...ification_and_inbox_timestamps_to_users.rb | 10 ++++++++ db/schema.rb | 4 +++- 9 files changed, 55 insertions(+), 31 deletions(-) create mode 100644 app/models/user/notification_methods.rb create mode 100644 db/migrate/20230225143633_add_notification_and_inbox_timestamps_to_users.rb diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 5c69fa9c..b8cbeb15 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -4,30 +4,6 @@ module ApplicationHelper include ApplicationHelper::GraphMethods include ApplicationHelper::TitleMethods - def inbox_count - return 0 unless user_signed_in? - - count = Inbox.select("COUNT(id) AS count") - .where(new: true) - .where(user_id: current_user.id) - .group(:user_id) - .order(:count) - .first - return nil if count.nil? - return nil unless count.count.positive? - - count.count - end - - def notification_count - return 0 unless user_signed_in? - - count = Notification.for(current_user).where(new: true).count - return nil unless count.positive? - - count - end - def privileged?(user) !current_user.nil? && ((current_user == user) || current_user.mod?) end diff --git a/app/models/inbox.rb b/app/models/inbox.rb index 8c8c602f..41372957 100644 --- a/app/models/inbox.rb +++ b/app/models/inbox.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Inbox < ApplicationRecord - belongs_to :user + belongs_to :user, touch: :inbox_updated_at belongs_to :question attr_accessor :returning @@ -29,7 +29,6 @@ class Inbox < ApplicationRecord def as_push_notification { - type: :inbox, title: I18n.t( "frontend.push_notifications.inbox.title", user: if question.author_is_anonymous @@ -39,7 +38,10 @@ class Inbox < ApplicationRecord end ), icon: question.author_is_anonymous ? "/icons/maskable_icon_x128.png" : question.user.profile_picture.url(:small), - body: question.content.truncate(Question::SHORT_QUESTION_MAX_LENGTH) + body: question.content.truncate(Question::SHORT_QUESTION_MAX_LENGTH), + data: { + click_url: "/inbox", + } } end end diff --git a/app/models/notification.rb b/app/models/notification.rb index 60890d17..34be42e1 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Notification < ApplicationRecord - belongs_to :recipient, class_name: "User" + belongs_to :recipient, class_name: "User", touch: :notifications_updated_at belongs_to :target, polymorphic: true class << self diff --git a/app/models/user.rb b/app/models/user.rb index 725582ef..92427a5d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -8,6 +8,7 @@ class User < ApplicationRecord # rubocop:disable Metrics/ClassLength include User::AnswerMethods include User::BanMethods include User::InboxMethods + include User::NotificationMethods include User::QuestionMethods include User::PushNotificationMethods include User::ReactionMethods diff --git a/app/models/user/inbox_methods.rb b/app/models/user/inbox_methods.rb index 1d961ce2..15276579 100644 --- a/app/models/user/inbox_methods.rb +++ b/app/models/user/inbox_methods.rb @@ -12,4 +12,21 @@ module User::InboxMethods .order(:created_at) .reverse_order end + + def unread_inbox_count + Rails.cache.fetch("#{inbox_cache_key}/unread_inbox_count") do + count = Inbox.select("COUNT(id) AS count") + .where(new: true) + .where(user_id: id) + .group(:user_id) + .order(:count) + .first + return nil if count.nil? + return nil unless count.count.positive? + + count.count + end + end + + def inbox_cache_key = "#{cache_key}-#{inbox_updated_at}" end diff --git a/app/models/user/notification_methods.rb b/app/models/user/notification_methods.rb new file mode 100644 index 00000000..d123f121 --- /dev/null +++ b/app/models/user/notification_methods.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module User::NotificationMethods + def unread_notification_count + Rails.cache.fetch("#{notification_cache_key}/unread_notification_count") do + count = Notification.for(self).where(new: true).count + return nil unless count.positive? + + count + end + end + + def notification_cache_key = "#{cache_key}-#{notifications_updated_at}" +end diff --git a/app/views/navigation/_main.html.haml b/app/views/navigation/_main.html.haml index 989717df..76baee17 100644 --- a/app/views/navigation/_main.html.haml +++ b/app/views/navigation/_main.html.haml @@ -1,6 +1,8 @@ - notifications = Notification.for(current_user).where(new: true).includes([:target]).limit(4) -= render 'navigation/desktop', notifications: notifications -= render 'navigation/mobile', notifications: notifications +- inbox_count = current_user.unread_inbox_count +- notification_count = current_user.unread_notification_count += render 'navigation/desktop', notifications:, inbox_count:, notification_count: += render 'navigation/mobile', inbox_count:, notification_count: = render 'modal/ask' %button.btn.btn-primary.btn-fab.d-block.d-lg-none.d-print-none{ data: { bs_target: "#modal-ask-followers", bs_toggle: :modal }, type: "button" } diff --git a/db/migrate/20230225143633_add_notification_and_inbox_timestamps_to_users.rb b/db/migrate/20230225143633_add_notification_and_inbox_timestamps_to_users.rb new file mode 100644 index 00000000..953a9207 --- /dev/null +++ b/db/migrate/20230225143633_add_notification_and_inbox_timestamps_to_users.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class AddNotificationAndInboxTimestampsToUsers < ActiveRecord::Migration[6.1] + def change + change_table :users, bulk: true do |t| + t.timestamp :notifications_updated_at + t.timestamp :inbox_updated_at + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 4d54a933..3cabc240 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_02_18_142952) do +ActiveRecord::Schema.define(version: 2023_02_25_143633) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -364,6 +364,8 @@ ActiveRecord::Schema.define(version: 2023_02_18_142952) do t.boolean "sharing_enabled", default: false t.boolean "sharing_autoclose", default: false t.string "sharing_custom_url" + t.datetime "notifications_updated_at" + t.datetime "inbox_updated_at" t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true t.index ["email"], name: "index_users_on_email", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true