From fbeb1ae993451fecc2bcbcb6cb1ee6ed65ae106d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 5 Oct 2026 12:35:35 +0000 Subject: [PATCH] Query sidebar directs and shared rooms separately Load visible direct memberships ordered by room recency and other rooms ordered by name, instead of hydrating every membership and splitting them in Ruby. Fixes #307. Co-authored-by: Thomas Klemm --- app/controllers/users/sidebars_controller.rb | 5 +++-- app/models/membership.rb | 1 + .../users/sidebars_controller_test.rb | 21 +++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/app/controllers/users/sidebars_controller.rb b/app/controllers/users/sidebars_controller.rb index ce998e8..4943a34 100644 --- a/app/controllers/users/sidebars_controller.rb +++ b/app/controllers/users/sidebars_controller.rb @@ -2,8 +2,9 @@ class Users::SidebarsController < ApplicationController DIRECT_PLACEHOLDERS = 20 def show - @direct_memberships, @other_memberships = Current.user.memberships.visible.with_ordered_room.partition { |membership| membership.room.direct? } - @direct_memberships = @direct_memberships.sort_by { |membership| membership.room.updated_at }.reverse + visible_memberships = Current.user.memberships.visible + @direct_memberships = visible_memberships.with_direct_rooms + @other_memberships = visible_memberships.without_direct_rooms.with_ordered_room @direct_placeholder_users = find_direct_placeholder_users end diff --git a/app/models/membership.rb b/app/models/membership.rb index 74c225b..da7a0a6 100644 --- a/app/models/membership.rb +++ b/app/models/membership.rb @@ -9,6 +9,7 @@ class Membership < ApplicationRecord enum :involvement, %w[ invisible nothing mentions everything ].index_by(&:itself), prefix: :involved_in scope :with_ordered_room, -> { includes(:room).joins(:room).order("LOWER(rooms.name)") } + scope :with_direct_rooms, -> { includes(:room).joins(:room).where(rooms: { type: "Rooms::Direct" }).order(rooms: { updated_at: :desc }) } scope :without_direct_rooms, -> { joins(:room).where.not(room: { type: "Rooms::Direct" }) } scope :visible, -> { where.not(involvement: :invisible) } diff --git a/test/controllers/users/sidebars_controller_test.rb b/test/controllers/users/sidebars_controller_test.rb index 50caae7..5ed957a 100644 --- a/test/controllers/users/sidebars_controller_test.rb +++ b/test/controllers/users/sidebars_controller_test.rb @@ -27,4 +27,25 @@ class Users::SidebarsControllerTest < ActionDispatch::IntegrationTest get user_sidebar_url assert_select ".unread", count: users(:david).memberships.reject { |m| m.room.direct? || !m.unread? }.count end + + test "directs are queried separately and ordered by room recency, not name" do + older = rooms(:david_and_jason) + newer = rooms(:david_and_kevin) + older.update_column :updated_at, 2.days.ago + newer.update_column :updated_at, 1.minute.ago + + get user_sidebar_url + + assert_operator @response.body.index(dom_id(newer, :list)), :<, @response.body.index(dom_id(older, :list)) + end + + test "shared rooms stay ordered by name" do + get user_sidebar_url + + shared_ids = users(:david).rooms.without_directs.sort_by { |room| room.name.downcase }.map { |room| dom_id(room, :list) } + positions = shared_ids.map { |id| @response.body.index(id) } + + assert positions.all? + assert_equal positions.sort, positions + end end