diff --git a/app/models/topic.rb b/app/models/topic.rb index 961d8f2..588971f 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -49,6 +49,61 @@ def participant_aliases(limit: 10) participants end + def participant_alias_stats(limit: 10) + stats = messages.group(:sender_id) + .select('sender_id, COUNT(*) as message_count, MAX(messages.created_at) AS last_at') + .order('message_count DESC') + .limit(50) + .index_by(&:sender_id) + + first_sender = messages.order(:created_at).first&.sender + last_sender = messages.order(:created_at).last&.sender + + missing_ids = [first_sender&.id, last_sender&.id].compact.uniq - stats.keys + if missing_ids.any? + extra_stats = messages.where(sender_id: missing_ids) + .group(:sender_id) + .select('sender_id, COUNT(*) as message_count, MAX(messages.created_at) AS last_at') + .index_by(&:sender_id) + stats.merge!(extra_stats) + end + + sender_ids = stats.keys + senders_by_id = Alias.includes(person: :contributor_memberships).where(id: sender_ids).index_by(&:id) + + entry_for = lambda do |alias_record| + return nil unless alias_record + + stat = stats[alias_record.id] + { + alias: alias_record, + message_count: stat&.read_attribute(:message_count)&.to_i, + last_at: stat&.read_attribute(:last_at) + } + end + + participants = [] + + participants << entry_for.call(first_sender) if first_sender + + first_and_last = [first_sender&.id, last_sender&.id].compact.uniq + other_senders = sender_ids - first_and_last + remaining = [limit - first_and_last.length, 0].max + other_participants = other_senders + .map { |id| senders_by_id[id] } + .compact + .sort_by { |s| -stats[s.id].read_attribute(:message_count).to_i } + .take(remaining) + + participants.concat(other_participants.map { |alias_record| entry_for.call(alias_record) }.compact) + + if last_sender && last_sender.id != first_sender&.id + participants << entry_for.call(last_sender) + end + + participants.compact + end + def has_contributor_activity? @has_contributor_activity ||= begin contributor_people = ContributorMembership.select(:person_id).distinct diff --git a/app/views/topics/_avatar_list.slim b/app/views/topics/_avatar_list.slim index bd814ee..0fc3cba 100644 --- a/app/views/topics/_avatar_list.slim +++ b/app/views/topics/_avatar_list.slim @@ -1,15 +1,23 @@ .participants .participants-avatars - participants.each do |participant| + - alias_record = participant[:alias] || participant + - next unless alias_record + - message_count = participant[:message_count] + - last_at = participant[:last_at] + - tooltip_parts = [] + - tooltip_parts << pluralize(message_count, "message") if message_count + - tooltip_parts << "last #{smart_time_display(last_at)}" if last_at + - role_label = alias_record.contributor_badge || "User" + - tooltip_parts << role_label + - badge_text = tooltip_parts.any? ? tooltip_parts.join(", ") : "#{alias_record.name} (#{role_label})" - css_classes = ["participant-avatar"] - - css_classes << "is-core-team" if participant.core_team? - - css_classes << "is-committer" if !participant.core_team? && participant.committer? - - css_classes << "is-major-contributor" if !participant.core_team? && !participant.committer? && participant.major_contributor? - - css_classes << "is-significant-contributor" if !participant.core_team? && !participant.committer? && !participant.major_contributor? && participant.significant_contributor? - - css_classes << "is-past-contributor" if participant.past_contributor? - - role_label = participant.contributor_badge || "User" - - badge_text = "#{participant.name} (#{role_label})" - = link_to person_path(participant.email), class: "participant-avatar-link" do - = image_tag participant.display_gravatar_url(size: 32), class: css_classes.join(" "), alt: participant.name, title: badge_text + - css_classes << "is-core-team" if alias_record.core_team? + - css_classes << "is-committer" if !alias_record.core_team? && alias_record.committer? + - css_classes << "is-major-contributor" if !alias_record.core_team? && !alias_record.committer? && alias_record.major_contributor? + - css_classes << "is-significant-contributor" if !alias_record.core_team? && !alias_record.committer? && !alias_record.major_contributor? && alias_record.significant_contributor? + - css_classes << "is-past-contributor" if alias_record.past_contributor? + = link_to person_path(alias_record.email), class: "participant-avatar-link" do + = image_tag alias_record.display_gravatar_url(size: 32), class: css_classes.join(" "), alt: alias_record.name, title: badge_text - if total_participants > participants.count span.participants-count +#{total_participants - participants.count} diff --git a/app/views/topics/_topic_row_user.html.slim b/app/views/topics/_topic_row_user.html.slim index 229f1fc..037379c 100644 --- a/app/views/topics/_topic_row_user.html.slim +++ b/app/views/topics/_topic_row_user.html.slim @@ -13,7 +13,7 @@ tr id=dom_id(topic) class="topic-row topic-#{state[:status] || 'new'}" data-topi .activity-replies = pluralize(replies_count, "reply") .activity-time title=absolute_time_display(last_message.created_at) = smart_time_display(last_message.created_at) td.topic-participants data-label="Participants" - - participants = topic.participant_aliases(limit: 5) + - participants = topic.participant_alias_stats(limit: 5) - participant_count = topic.participant_count = render partial: "avatar_list", locals: { participants: participants, total_participants: participant_count } - if topic.has_contributor_activity? diff --git a/app/views/topics/_topics.html.slim b/app/views/topics/_topics.html.slim index bff2014..c729074 100644 --- a/app/views/topics/_topics.html.slim +++ b/app/views/topics/_topics.html.slim @@ -8,7 +8,7 @@ .activity-replies = pluralize(replies_count, "reply") .activity-time title=absolute_time_display(last_message.created_at) = smart_time_display(last_message.created_at) td.topic-participants data-label="Participants" - - participants = topic.participant_aliases(limit: 5) + - participants = topic.participant_alias_stats(limit: 5) - participant_count = topic.participant_count = render partial: "avatar_list", locals: { participants: participants, total_participants: participant_count } - if topic.has_contributor_activity?