mirror of
https://github.com/discourse/discourse.git
synced 2025-03-23 05:45:42 +08:00
FEATURE: Chat thread header indicator improvements (#21807)
This changes the thread header positioning of the unread indicator to match the designs based on the route: 1. When the channel is open, show the indicator of # unread threads with the icon 2. When the threads list is open, show no indicator since you are on the list and can see which threads are unread 3. When a single thread is open, show the unread threads indicator along with a left < back button, with a label to show that this goes back to ongoing discussions Drawer changes to come in another PR.
This commit is contained in:
parent
c2493a8f1c
commit
6ec6cfccf8
@ -1,6 +1,6 @@
|
|||||||
<div class="chat-drawer-header__right-actions">
|
<div class="chat-drawer-header__right-actions">
|
||||||
<div class="chat-drawer-header__top-line">
|
<div class="chat-drawer-header__top-line">
|
||||||
{{#if this.chat.activeChannel.threadingEnabled}}
|
{{#if this.showThreadsListButton}}
|
||||||
<Chat::Thread::ThreadsListButton @channel={{this.chat.activeChannel}} />
|
<Chat::Thread::ThreadsListButton @channel={{this.chat.activeChannel}} />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
@ -3,5 +3,8 @@ import { inject as service } from "@ember/service";
|
|||||||
|
|
||||||
export default class ChatDrawerHeaderRightActions extends Component {
|
export default class ChatDrawerHeaderRightActions extends Component {
|
||||||
@service chat;
|
@service chat;
|
||||||
@service chatStateManager;
|
|
||||||
|
get showThreadsListButton() {
|
||||||
|
return this.chat.activeChannel?.threadingEnabled;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if @channel.threadingEnabled}}
|
{{#if this.showThreadsListButton}}
|
||||||
<Chat::Thread::ThreadsListButton @channel={{@channel}} />
|
<Chat::Thread::ThreadsListButton @channel={{@channel}} />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,8 +4,17 @@ import Component from "@glimmer/component";
|
|||||||
export default class ChatFullPageHeader extends Component {
|
export default class ChatFullPageHeader extends Component {
|
||||||
@service site;
|
@service site;
|
||||||
@service chatStateManager;
|
@service chatStateManager;
|
||||||
|
@service router;
|
||||||
|
|
||||||
get displayed() {
|
get displayed() {
|
||||||
return this.args.displayed ?? true;
|
return this.args.displayed ?? true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get showThreadsListButton() {
|
||||||
|
return (
|
||||||
|
this.args.channel.threadingEnabled &&
|
||||||
|
this.router.currentRoute.name !== "chat.channel.threads" &&
|
||||||
|
this.router.currentRoute.name !== "chat.channel.thread"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
{{#if this.showUnreadIndicator}}
|
{{#if this.showUnreadIndicator}}
|
||||||
<div class="chat-thread-header-unread-indicator">
|
<div
|
||||||
|
class="chat-thread-header-unread-indicator"
|
||||||
|
aria-label={{i18n "chat.unread_threads_count" count=this.unreadCountLabel}}
|
||||||
|
title={{i18n "chat.unread_threads_count" count=this.unreadCountLabel}}
|
||||||
|
>
|
||||||
<div class="chat-thread-header-unread-indicator__number-wrap">
|
<div class="chat-thread-header-unread-indicator__number-wrap">
|
||||||
<div
|
<div
|
||||||
class="chat-thread-header-unread-indicator__number"
|
class="chat-thread-header-unread-indicator__number"
|
||||||
|
@ -1,12 +1,19 @@
|
|||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
|
||||||
export default class ChatThreadHeaderUnreadIndicator extends Component {
|
export default class ChatThreadHeaderUnreadIndicator extends Component {
|
||||||
|
@service currentUser;
|
||||||
|
|
||||||
|
get currentUserInDnD() {
|
||||||
|
return this.currentUser.isInDoNotDisturb();
|
||||||
|
}
|
||||||
|
|
||||||
get unreadCount() {
|
get unreadCount() {
|
||||||
return this.args.channel.unreadThreadCount;
|
return this.args.channel.unreadThreadCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
get showUnreadIndicator() {
|
get showUnreadIndicator() {
|
||||||
return this.unreadCount > 0;
|
return !this.currentUserInDnD && this.unreadCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
get unreadCountLabel() {
|
get unreadCountLabel() {
|
||||||
|
@ -1,4 +1,18 @@
|
|||||||
<div class="chat-thread-header">
|
<div class="chat-thread-header">
|
||||||
|
<div class="chat-thread-header__left-buttons">
|
||||||
|
{{#if @thread}}
|
||||||
|
<LinkTo
|
||||||
|
class="chat-thread__back-to-list btn-flat btn btn-icon no-text"
|
||||||
|
@route="chat.channel.threads"
|
||||||
|
@models={{@channel.routeModels}}
|
||||||
|
title={{i18n "chat.return_to_threads_list"}}
|
||||||
|
>
|
||||||
|
<Chat::Thread::HeaderUnreadIndicator @channel={{@channel}} />
|
||||||
|
{{d-icon "chevron-left"}}
|
||||||
|
</LinkTo>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
<span class="chat-thread-header__label overflow-ellipsis">
|
<span class="chat-thread-header__label overflow-ellipsis">
|
||||||
{{replace-emoji this.label}}
|
{{replace-emoji this.label}}
|
||||||
</span>
|
</span>
|
||||||
|
@ -9,7 +9,5 @@
|
|||||||
>
|
>
|
||||||
{{d-icon "discourse-threads"}}
|
{{d-icon "discourse-threads"}}
|
||||||
|
|
||||||
{{#unless this.currentUserInDnD}}
|
<Chat::Thread::HeaderUnreadIndicator @channel={{@channel}} />
|
||||||
<Chat::Thread::HeaderUnreadIndicator @channel={{@channel}} />
|
|
||||||
{{/unless}}
|
|
||||||
</LinkTo>
|
</LinkTo>
|
@ -1,10 +1,3 @@
|
|||||||
import { inject as service } from "@ember/service";
|
|
||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
|
|
||||||
export default class ChatThreadsListButton extends Component {
|
export default class ChatThreadsListButton extends Component {}
|
||||||
@service currentUser;
|
|
||||||
|
|
||||||
get currentUserInDnD() {
|
|
||||||
return this.currentUser.isInDoNotDisturb();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border: 1px solid var(--blend-primary-secondary-5);
|
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
|
||||||
@include chat-channel-header-button;
|
@include chat-channel-header-button;
|
||||||
|
|
||||||
@ -11,6 +11,12 @@
|
|||||||
.d-icon {
|
.d-icon {
|
||||||
color: var(--tertiary-med-or-tertiary);
|
color: var(--tertiary-med-or-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.d-icon {
|
||||||
|
color: var(--tertiary-med-or-tertiary);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.d-icon {
|
.d-icon {
|
||||||
|
@ -6,10 +6,21 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
padding-inline: 0.5rem;
|
||||||
padding-inline: 1rem;
|
|
||||||
|
.chat-thread__back-to-list {
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
&__buttons {
|
&__buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__left-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
.chat-thread-list-item {
|
.chat-thread-list-item {
|
||||||
@include thread-list-item;
|
@include thread-list-item;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 0.5rem 0.25rem 0.25rem;
|
margin: 0.25rem;
|
||||||
|
|
||||||
& + .chat-thread-list-item {
|
& + .chat-thread-list-item {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
@ -251,6 +251,9 @@ en:
|
|||||||
select: "Select"
|
select: "Select"
|
||||||
return_to_list: "Return to channels list"
|
return_to_list: "Return to channels list"
|
||||||
return_to_threads_list: "Return to ongoing discussions"
|
return_to_threads_list: "Return to ongoing discussions"
|
||||||
|
unread_threads_count:
|
||||||
|
one: "You have %{count} unread discussion"
|
||||||
|
other: "You have %{count} unread discussions"
|
||||||
scroll_to_bottom: "Scroll to bottom"
|
scroll_to_bottom: "Scroll to bottom"
|
||||||
scroll_to_new_messages: "See new messages"
|
scroll_to_new_messages: "See new messages"
|
||||||
sound:
|
sound:
|
||||||
|
@ -24,6 +24,21 @@ module PageObjects
|
|||||||
header.find(".chat-thread__close").click
|
header.find(".chat-thread__close").click
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def back_to_list
|
||||||
|
header.find(".chat-thread__back-to-list").click
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_no_unread_list_indicator?
|
||||||
|
has_no_css?(".chat-thread__back-to-list .chat-thread-header-unread-indicator")
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_unread_list_indicator?(count:)
|
||||||
|
has_css?(
|
||||||
|
".chat-thread__back-to-list .chat-thread-header-unread-indicator .chat-thread-header-unread-indicator__number-wrap",
|
||||||
|
text: count.to_s,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def has_no_loading_skeleton?
|
def has_no_loading_skeleton?
|
||||||
has_no_css?(".chat-thread__messages .chat-skeleton")
|
has_no_css?(".chat-thread__messages .chat-skeleton")
|
||||||
end
|
end
|
||||||
|
@ -39,28 +39,27 @@ describe "Thread tracking state | full page", type: :system, js: true do
|
|||||||
chat_page.visit_channel(channel)
|
chat_page.visit_channel(channel)
|
||||||
channel_page.open_thread_list
|
channel_page.open_thread_list
|
||||||
thread_list_page.item_by_id(thread.id).click
|
thread_list_page.item_by_id(thread.id).click
|
||||||
expect(channel_page).to have_no_unread_thread_indicator
|
expect(thread_page).to have_no_unread_list_indicator
|
||||||
channel_page.open_thread_list
|
thread_page.back_to_list
|
||||||
expect(thread_list_page).to have_no_unread_item(thread.id)
|
expect(thread_list_page).to have_no_unread_item(thread.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "shows unread indicators for the header icon and the list when a new unread arrives" do
|
it "shows unread indicators for the header of the list when a new unread arrives" do
|
||||||
message_1.trash!
|
message_1.trash!
|
||||||
chat_page.visit_channel(channel)
|
chat_page.visit_channel(channel)
|
||||||
channel_page.open_thread_list
|
channel_page.open_thread_list
|
||||||
expect(channel_page).to have_no_unread_thread_indicator
|
|
||||||
expect(thread_list_page).to have_no_unread_item(thread.id)
|
expect(thread_list_page).to have_no_unread_item(thread.id)
|
||||||
Fabricate(:chat_message, chat_channel: channel, thread: thread)
|
Fabricate(:chat_message, chat_channel: channel, thread: thread)
|
||||||
expect(channel_page).to have_unread_thread_indicator(count: 1)
|
|
||||||
expect(thread_list_page).to have_unread_item(thread.id)
|
expect(thread_list_page).to have_unread_item(thread.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not change the unread indicator for the header icon when the user is not a member of the thread" do
|
it "does not change the unread indicator for the header icon when the user is not a member of the thread" do
|
||||||
thread.remove(current_user)
|
thread.remove(current_user)
|
||||||
chat_page.visit_channel(channel)
|
chat_page.visit_channel(channel)
|
||||||
channel_page.open_thread_list
|
expect(channel_page).to have_no_unread_thread_indicator
|
||||||
Fabricate(:chat_message, chat_channel: channel, thread: thread)
|
Fabricate(:chat_message, chat_channel: channel, thread: thread)
|
||||||
expect(channel_page).to have_no_unread_thread_indicator
|
expect(channel_page).to have_no_unread_thread_indicator
|
||||||
|
channel_page.open_thread_list
|
||||||
expect(thread_list_page).to have_no_unread_item(thread.id)
|
expect(thread_list_page).to have_no_unread_item(thread.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user