mirror of
https://github.com/discourse/discourse.git
synced 2025-06-07 22:34:44 +08:00
UX: remove excerpt + style change to thread list item (#23776)
UX changes to thread item: - drop "last reply" timestamp copy - drop last reply excerpt - show up 9+OP members Co-authored-by: David Battersby <info@davidbattersby.com>
This commit is contained in:
@ -2,12 +2,15 @@
|
|||||||
|
|
||||||
module Chat
|
module Chat
|
||||||
# Builds a query to find the total count of participants for one
|
# Builds a query to find the total count of participants for one
|
||||||
# or more threads (on a per-thread basis), as well as up to 3
|
# or more threads (on a per-thread basis), as well as up to 10
|
||||||
# participants in the thread. The participants will be made up
|
# participants in the thread. The participants will be made up
|
||||||
# of:
|
# of:
|
||||||
#
|
#
|
||||||
# - Participant 1 & 2 - The most frequent participants in the thread.
|
# The most frequent participants in the thread:
|
||||||
# - Participant 3 - The most recent participant in the thread.
|
# - Participant 1-2 (preview)
|
||||||
|
# - Participant 1-9 (thread list)
|
||||||
|
# The most recent participant in the thread.
|
||||||
|
# - Participant 10
|
||||||
#
|
#
|
||||||
# This result should be cached to avoid unnecessary queries,
|
# This result should be cached to avoid unnecessary queries,
|
||||||
# since the participants will not often change for a thread,
|
# since the participants will not often change for a thread,
|
||||||
@ -15,8 +18,9 @@ module Chat
|
|||||||
# count it is not a big deal.
|
# count it is not a big deal.
|
||||||
class ThreadParticipantQuery
|
class ThreadParticipantQuery
|
||||||
# @param thread_ids [Array<Integer>] The IDs of the threads to query.
|
# @param thread_ids [Array<Integer>] The IDs of the threads to query.
|
||||||
|
# @param preview [Boolean] Determines the number of participants to return.
|
||||||
# @return [Hash<Integer, Hash>] A hash of thread IDs to participant data.
|
# @return [Hash<Integer, Hash>] A hash of thread IDs to participant data.
|
||||||
def self.call(thread_ids:)
|
def self.call(thread_ids:, preview: true)
|
||||||
return {} if thread_ids.blank?
|
return {} if thread_ids.blank?
|
||||||
|
|
||||||
# We only want enough data for BasicUserSerializer, since the participants
|
# We only want enough data for BasicUserSerializer, since the participants
|
||||||
@ -60,6 +64,8 @@ module Chat
|
|||||||
hash
|
hash
|
||||||
end
|
end
|
||||||
|
|
||||||
|
total_participants = preview ? 2 : 9
|
||||||
|
|
||||||
thread_participants = {}
|
thread_participants = {}
|
||||||
thread_participant_stats.each do |thread_participant_stat|
|
thread_participant_stats.each do |thread_participant_stat|
|
||||||
thread_id = thread_participant_stat.thread_id
|
thread_id = thread_participant_stat.thread_id
|
||||||
@ -69,7 +75,7 @@ module Chat
|
|||||||
|
|
||||||
# If we want to return more of the top N users in the thread we
|
# If we want to return more of the top N users in the thread we
|
||||||
# can just increase the number here.
|
# can just increase the number here.
|
||||||
if thread_participants[thread_id][:users].length < 2 &&
|
if thread_participants[thread_id][:users].length < total_participants &&
|
||||||
thread_participant_stat.user_id != most_recent_participants[thread_id][:id]
|
thread_participant_stat.user_id != most_recent_participants[thread_id][:id]
|
||||||
thread_participants[thread_id][:users].push(
|
thread_participants[thread_id][:users].push(
|
||||||
{
|
{
|
||||||
|
@ -135,7 +135,8 @@ module Chat
|
|||||||
end
|
end
|
||||||
|
|
||||||
def fetch_participants(threads:, **)
|
def fetch_participants(threads:, **)
|
||||||
context.participants = ::Chat::ThreadParticipantQuery.call(thread_ids: threads.map(&:id))
|
context.participants =
|
||||||
|
::Chat::ThreadParticipantQuery.call(thread_ids: threads.map(&:id), preview: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_load_more_url(contract:, **)
|
def build_load_more_url(contract:, **)
|
||||||
|
@ -26,33 +26,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="chat-thread-list-item__body">
|
|
||||||
<span class="chat-thread-list-item__last-reply-author">
|
|
||||||
@{{@thread.preview.lastReplyUser.username}}:
|
|
||||||
</span>
|
|
||||||
<span class="chat-thread-list-item__last-reply-excerpt">
|
|
||||||
{{replace-emoji (html-safe @thread.preview.lastReplyExcerpt)}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="chat-thread-list-item__metadata">
|
<div class="chat-thread-list-item__metadata">
|
||||||
|
|
||||||
|
<div class="chat-thread-list-item__members">
|
||||||
|
<Chat::UserAvatar
|
||||||
|
@user={{@thread.originalMessage.user}}
|
||||||
|
@showPresence={{false}}
|
||||||
|
/>
|
||||||
|
<Chat::Thread::Participants
|
||||||
|
@thread={{@thread}}
|
||||||
|
@includeOriginalMessageUser={{false}}
|
||||||
|
class="chat-thread-list-item__participants"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="chat-thread-list-item__last-reply-timestamp">
|
<div class="chat-thread-list-item__last-reply-timestamp">
|
||||||
{{#if @thread.preview.lastReplyCreatedAt}}
|
{{#if @thread.preview.lastReplyCreatedAt}}
|
||||||
<span>{{i18n "chat.thread.last_reply"}}</span>
|
|
||||||
{{format-date @thread.preview.lastReplyCreatedAt leaveAgo="true"}}
|
{{format-date @thread.preview.lastReplyCreatedAt leaveAgo="true"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Chat::UserAvatar
|
|
||||||
@user={{@thread.originalMessage.user}}
|
|
||||||
@showPresence={{false}}
|
|
||||||
/>
|
|
||||||
<Chat::Thread::Participants
|
|
||||||
@thread={{@thread}}
|
|
||||||
@includeOriginalMessageUser={{false}}
|
|
||||||
class="chat-thread-list-item__participants"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
.touch & {
|
.touch & {
|
||||||
&:active {
|
&:active {
|
||||||
background-color: var(--d-hover);
|
background-color: var(--d-hover);
|
||||||
border: 1px solid var(--primary-300);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,13 +26,21 @@
|
|||||||
&:hover,
|
&:hover,
|
||||||
&:active {
|
&:active {
|
||||||
background-color: var(--d-hover);
|
background-color: var(--d-hover);
|
||||||
border: 1px solid var(--primary-300);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__participants {
|
&__members {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__participants {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-right: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__main {
|
&__main {
|
||||||
@ -63,47 +70,50 @@
|
|||||||
&__metadata {
|
&__metadata {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
gap: 0.5rem;
|
||||||
|
|
||||||
.chat-user-avatar {
|
.chat-user-avatar {
|
||||||
|
&__container {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
.avatar {
|
.avatar {
|
||||||
border: 1px solid var(--primary-very-low);
|
border: 2px solid var(--primary-very-low);
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.chat-thread-list-item:hover & {
|
||||||
|
border-color: var(--d-hover);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
margin-right: -8px;
|
margin-right: -6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.chat-thread-participants__avatar-group {
|
||||||
|
overflow: hidden;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__participants {
|
|
||||||
margin-right: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__last-reply-timestamp {
|
&__last-reply-timestamp {
|
||||||
|
flex-shrink: 0;
|
||||||
color: var(--primary-medium);
|
color: var(--primary-medium);
|
||||||
font-size: var(--font-down-2);
|
font-size: var(--font-down-2);
|
||||||
@include ellipsis;
|
@include ellipsis;
|
||||||
margin-right: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
font-weight: bold;
|
|
||||||
@include ellipsis;
|
@include ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__last-reply-excerpt {
|
|
||||||
font-size: var(--font-down-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__unread-indicator {
|
&__unread-indicator {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
RSpec.describe Chat::ThreadParticipantQuery do
|
RSpec.describe Chat::ThreadParticipantQuery do
|
||||||
fab!(:thread_1) { Fabricate(:chat_thread) }
|
fab!(:thread_1) { Fabricate(:chat_thread) }
|
||||||
fab!(:thread_2) { Fabricate(:chat_thread) }
|
fab!(:thread_2) { Fabricate(:chat_thread) }
|
||||||
|
fab!(:thread_3) { Fabricate(:chat_thread) }
|
||||||
|
|
||||||
context "when users have messaged in the thread" do
|
context "when users have messaged in the thread" do
|
||||||
fab!(:user_1) { Fabricate(:user) }
|
fab!(:user_1) { Fabricate(:user) }
|
||||||
@ -34,10 +35,8 @@ RSpec.describe Chat::ThreadParticipantQuery do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not return more than 3 thread participants" do
|
it "does not return more than 3 thread participants by default" do
|
||||||
other_user = Fabricate(:user)
|
thread_1.add(Fabricate(:user))
|
||||||
thread_1.add(other_user)
|
|
||||||
Fabricate(:chat_message, thread: thread_1, user: other_user)
|
|
||||||
result = described_class.call(thread_ids: [thread_1.id])
|
result = described_class.call(thread_ids: [thread_1.id])
|
||||||
expect(result[thread_1.id][:users].length).to eq(3)
|
expect(result[thread_1.id][:users].length).to eq(3)
|
||||||
end
|
end
|
||||||
@ -105,4 +104,27 @@ RSpec.describe Chat::ThreadParticipantQuery do
|
|||||||
expect(result[thread_2.id][:total_count]).to eq(1)
|
expect(result[thread_2.id][:total_count]).to eq(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "when using preview false" do
|
||||||
|
1..9.times do |i|
|
||||||
|
user = "user_#{i}".to_sym
|
||||||
|
fab!(user) { Fabricate(:user) }
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
1..9.times do |i|
|
||||||
|
user = "user_#{i}".to_sym
|
||||||
|
thread_3.add(public_send(user))
|
||||||
|
Fabricate(:chat_message, thread: thread_3, user: public_send(user))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not return more than 10 thread participants when preview is false" do
|
||||||
|
other_user = Fabricate(:user)
|
||||||
|
thread_3.add(other_user)
|
||||||
|
Fabricate(:chat_message, thread: thread_3, user: other_user)
|
||||||
|
result = described_class.call(thread_ids: [thread_3.id], preview: false)
|
||||||
|
expect(result[thread_3.id][:users].length).to eq(10)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Reference in New Issue
Block a user