FIX: show urgent badge for mentions in DM threads (#29821)
When thread tracking level is Normal in a DM channel, we should still show notification badges to the mentioned user.
This commit is contained in:
parent
1497b298d2
commit
3cde55b76f
|
@ -1,7 +1,8 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { service } from "@ember/service";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import { hasChatIndicator } from "../lib/chat-user-preferences";
|
||||
|
||||
const MAX_UNREAD_COUNT = 99;
|
||||
|
||||
export default class ChatChannelUnreadIndicator extends Component {
|
||||
@service chat;
|
||||
|
@ -10,48 +11,41 @@ export default class ChatChannelUnreadIndicator extends Component {
|
|||
|
||||
get showUnreadIndicator() {
|
||||
return (
|
||||
this.args.channel.tracking.unreadCount > 0 ||
|
||||
this.args.channel.tracking.mentionCount > 0 ||
|
||||
this.args.channel.unreadThreadsCountSinceLastViewed > 0
|
||||
this.args.channel.tracking.unreadCount +
|
||||
this.args.channel.tracking.mentionCount +
|
||||
this.args.channel.unreadThreadsCountSinceLastViewed >
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
get publicUrgentCount() {
|
||||
return (
|
||||
this.args.channel.tracking.mentionCount +
|
||||
this.args.channel.tracking.watchedThreadsUnreadCount
|
||||
);
|
||||
}
|
||||
|
||||
get directUrgentCount() {
|
||||
return (
|
||||
this.args.channel.tracking.unreadCount +
|
||||
this.args.channel.tracking.mentionCount +
|
||||
this.args.channel.tracking.watchedThreadsUnreadCount
|
||||
);
|
||||
}
|
||||
|
||||
get urgentCount() {
|
||||
if (this.hasChannelMentions) {
|
||||
return this.args.channel.tracking.mentionCount;
|
||||
}
|
||||
if (this.hasWatchedThreads) {
|
||||
return this.args.channel.tracking.watchedThreadsUnreadCount;
|
||||
}
|
||||
return this.args.channel.tracking.unreadCount;
|
||||
return this.args.channel.isDirectMessageChannel
|
||||
? this.directUrgentCount
|
||||
: this.publicUrgentCount;
|
||||
}
|
||||
|
||||
get isUrgent() {
|
||||
if (this.onlyMentions) {
|
||||
return this.hasChannelMentions;
|
||||
}
|
||||
return (
|
||||
this.isDirectMessage || this.hasChannelMentions || this.hasWatchedThreads
|
||||
);
|
||||
return this.urgentCount > 0;
|
||||
}
|
||||
|
||||
get isDirectMessage() {
|
||||
return (
|
||||
this.args.channel.isDirectMessageChannel &&
|
||||
this.args.channel.tracking.unreadCount > 0
|
||||
);
|
||||
}
|
||||
|
||||
get hasChannelMentions() {
|
||||
return this.args.channel.tracking.mentionCount > 0;
|
||||
}
|
||||
|
||||
get hasWatchedThreads() {
|
||||
return this.args.channel.tracking.watchedThreadsUnreadCount > 0;
|
||||
}
|
||||
|
||||
get onlyMentions() {
|
||||
return hasChatIndicator(this.currentUser).ONLY_MENTIONS;
|
||||
get urgentBadgeCount() {
|
||||
let totalCount = this.urgentCount;
|
||||
return totalCount > MAX_UNREAD_COUNT ? `${MAX_UNREAD_COUNT}+` : totalCount;
|
||||
}
|
||||
|
||||
<template>
|
||||
|
@ -62,9 +56,9 @@ export default class ChatChannelUnreadIndicator extends Component {
|
|||
(if this.isUrgent "-urgent")
|
||||
}}
|
||||
>
|
||||
<div class="chat-channel-unread-indicator__number">{{#if
|
||||
this.isUrgent
|
||||
}}{{this.urgentCount}}{{else}} {{/if}}</div>
|
||||
<div class="chat-channel-unread-indicator__number">
|
||||
{{#if this.isUrgent}}{{this.urgentBadgeCount}}{{else}} {{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
|
|
@ -27,7 +27,10 @@ export default class FooterUnreadIndicator extends Component {
|
|||
if (this.badgeType === CHANNELS_TAB) {
|
||||
return this.chatTrackingStateManager.publicChannelMentionCount;
|
||||
} else if (this.badgeType === DMS_TAB) {
|
||||
return this.chatTrackingStateManager.directMessageUnreadCount;
|
||||
return (
|
||||
this.chatTrackingStateManager.directMessageUnreadCount +
|
||||
this.chatTrackingStateManager.directMessageMentionCount
|
||||
);
|
||||
} else if (this.badgeType === THREADS_TAB) {
|
||||
return this.chatTrackingStateManager.watchedThreadsUnreadCount;
|
||||
} else {
|
||||
|
|
|
@ -1,21 +1,40 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { service } from "@ember/service";
|
||||
import { gt, not } from "truth-helpers";
|
||||
import { not } from "truth-helpers";
|
||||
import ChannelTitle from "discourse/plugins/chat/discourse/components/channel-title";
|
||||
|
||||
export default class Channel extends Component {
|
||||
@service currentUser;
|
||||
|
||||
get tracking() {
|
||||
return this.args.item.tracking;
|
||||
}
|
||||
|
||||
get isUrgent() {
|
||||
return this.args.item.model.isDirectMessageChannel
|
||||
? this.hasUnreads || this.hasUrgent
|
||||
: this.hasUrgent;
|
||||
}
|
||||
|
||||
get hasUnreads() {
|
||||
return this.tracking?.unreadCount > 0;
|
||||
}
|
||||
|
||||
get hasUrgent() {
|
||||
return (
|
||||
this.args.item.model.isDirectMessageChannel ||
|
||||
(this.args.item.model.isCategoryChannel &&
|
||||
this.args.item.model.tracking.mentionCount > 0) ||
|
||||
(this.args.item.model.isCategoryChannel &&
|
||||
this.args.item.model.tracking.watchedThreadsUnreadCount > 0)
|
||||
this.tracking?.mentionCount > 0 ||
|
||||
this.tracking?.watchedThreadsUnreadCount > 0
|
||||
);
|
||||
}
|
||||
|
||||
get hasUnreadThreads() {
|
||||
return this.args.item.unread_thread_count > 0;
|
||||
}
|
||||
|
||||
get showIndicator() {
|
||||
return this.hasUnreads || this.hasUnreadThreads || this.hasUrgent;
|
||||
}
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="chat-message-creator__chatable -category-channel"
|
||||
|
@ -23,7 +42,7 @@ export default class Channel extends Component {
|
|||
>
|
||||
<ChannelTitle
|
||||
@channel={{@item.model}}
|
||||
@isUnread={{gt @item.tracking.unreadCount 0}}
|
||||
@isUnread={{this.showIndicator}}
|
||||
@isUrgent={{this.isUrgent}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -59,7 +59,14 @@ export default class ChatablesLoader {
|
|||
]
|
||||
.map((item) => {
|
||||
const chatable = ChatChatable.create(item);
|
||||
chatable.tracking = this.#injectTracking(chatable);
|
||||
const channel = this.#findChannel(chatable);
|
||||
|
||||
if (channel) {
|
||||
chatable.tracking = channel.tracking;
|
||||
chatable.unread_thread_count =
|
||||
channel.unreadThreadsCountSinceLastViewed;
|
||||
}
|
||||
|
||||
return chatable;
|
||||
})
|
||||
.slice(0, MAX_RESULTS);
|
||||
|
@ -74,24 +81,32 @@ export default class ChatablesLoader {
|
|||
let chatable;
|
||||
if (channel.chatable?.users?.length === 1) {
|
||||
chatable = ChatChatable.createUser(channel.chatable.users[0]);
|
||||
chatable.tracking = this.#injectTracking(chatable);
|
||||
} else {
|
||||
chatable = ChatChatable.createChannel(channel);
|
||||
chatable.tracking = channel.tracking;
|
||||
}
|
||||
|
||||
chatable.tracking = channel.tracking;
|
||||
chatable.unread_thread_count =
|
||||
channel.unreadThreadsCountSinceLastViewed;
|
||||
return chatable;
|
||||
})
|
||||
.filter(Boolean)
|
||||
.slice(0, MAX_RESULTS);
|
||||
}
|
||||
|
||||
#injectTracking(chatable) {
|
||||
if (!chatable.type === "channel") {
|
||||
#findChannel(chatable) {
|
||||
if (!["user", "channel"].includes(chatable.type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.chatChannelsManager.allChannels.find(
|
||||
(channel) => channel.id === chatable.model.id
|
||||
)?.tracking;
|
||||
const { allChannels } = this.chatChannelsManager;
|
||||
if (chatable.type === "user") {
|
||||
return allChannels.find(
|
||||
({ chatable: { users } }) =>
|
||||
users?.length === 1 && users[0].id === chatable.model.id
|
||||
);
|
||||
} else if (chatable.type === "channel") {
|
||||
return allChannels.find(({ id }) => id === chatable.model.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { service } from "@ember/service";
|
||||
import { modifier } from "ember-modifier";
|
||||
import { gt, not } from "truth-helpers";
|
||||
import { not } from "truth-helpers";
|
||||
import UserStatusMessage from "discourse/components/user-status-message";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import userStatus from "discourse/helpers/user-status";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import ChatUserAvatar from "discourse/plugins/chat/discourse/components/chat-user-avatar";
|
||||
|
@ -21,6 +22,22 @@ export default class ChatableUser extends Component {
|
|||
};
|
||||
});
|
||||
|
||||
get showIndicator() {
|
||||
return this.isUrgent || this.isUnread;
|
||||
}
|
||||
|
||||
get isUrgent() {
|
||||
return (
|
||||
this.args.item.tracking?.unreadCount > 0 ||
|
||||
this.args.item.tracking?.mentionCount > 0 ||
|
||||
this.args.item.tracking?.watchedThreadsUnreadCount > 0
|
||||
);
|
||||
}
|
||||
|
||||
get isUnread() {
|
||||
return this.args.item.unread_thread_count > 0;
|
||||
}
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="chat-message-creator__chatable -user"
|
||||
|
@ -29,8 +46,10 @@ export default class ChatableUser extends Component {
|
|||
<ChatUserAvatar @user={{@item.model}} @interactive={{false}} />
|
||||
<ChatUserDisplayName @user={{@item.model}} />
|
||||
|
||||
{{#if (gt @item.tracking.unreadCount 0)}}
|
||||
<div class="unread-indicator -urgent"></div>
|
||||
{{#if this.showIndicator}}
|
||||
<div
|
||||
class={{concatClass "unread-indicator" (if this.isUrgent "-urgent")}}
|
||||
></div>
|
||||
{{/if}}
|
||||
|
||||
{{userStatus @item.model currentUser=this.currentUser}}
|
||||
|
|
|
@ -293,7 +293,6 @@ export default {
|
|||
const SidebarChatDirectMessagesSectionLink = class extends BaseCustomSidebarSectionLink {
|
||||
route = "chat.channel";
|
||||
suffixType = "icon";
|
||||
suffixCSSClass = "urgent";
|
||||
hoverType = "icon";
|
||||
hoverValue = "xmark";
|
||||
hoverTitle = i18n("chat.direct_messages.close");
|
||||
|
@ -424,7 +423,18 @@ export default {
|
|||
}
|
||||
|
||||
get suffixValue() {
|
||||
return this.channel.tracking.unreadCount > 0 ? "circle" : "";
|
||||
return this.channel.tracking.unreadCount > 0 ||
|
||||
this.channel.unreadThreadsCountSinceLastViewed > 0
|
||||
? "circle"
|
||||
: "";
|
||||
}
|
||||
|
||||
get suffixCSSClass() {
|
||||
return this.channel.tracking.unreadCount > 0 ||
|
||||
this.channel.tracking.mentionCount > 0 ||
|
||||
this.channel.tracking.watchedThreadsUnreadCount > 0
|
||||
? "urgent"
|
||||
: "unread";
|
||||
}
|
||||
|
||||
get hoverAction() {
|
||||
|
|
|
@ -210,12 +210,12 @@ export default class ChatChannelsManager extends Service {
|
|||
a: {
|
||||
urgent:
|
||||
a.tracking.mentionCount + a.tracking.watchedThreadsUnreadCount,
|
||||
unread: a.tracking.unreadCount + a.threadsManager.unreadThreadCount,
|
||||
unread: a.tracking.unreadCount + a.unreadThreadsCountSinceLastViewed,
|
||||
},
|
||||
b: {
|
||||
urgent:
|
||||
b.tracking.mentionCount + b.tracking.watchedThreadsUnreadCount,
|
||||
unread: b.tracking.unreadCount + b.threadsManager.unreadThreadCount,
|
||||
unread: b.tracking.unreadCount + b.unreadThreadsCountSinceLastViewed,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -253,22 +253,26 @@ export default class ChatChannelsManager extends Service {
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (
|
||||
a.tracking.unreadCount + a.tracking.watchedThreadsUnreadCount > 0 ||
|
||||
b.tracking.unreadCount + b.tracking.watchedThreadsUnreadCount > 0
|
||||
) {
|
||||
return a.tracking.unreadCount + a.tracking.watchedThreadsUnreadCount >
|
||||
b.tracking.unreadCount + b.tracking.watchedThreadsUnreadCount
|
||||
? -1
|
||||
: 1;
|
||||
const aUrgent =
|
||||
a.tracking.unreadCount +
|
||||
a.tracking.mentionCount +
|
||||
a.tracking.watchedThreadsUnreadCount;
|
||||
|
||||
const bUrgent =
|
||||
b.tracking.unreadCount +
|
||||
b.tracking.mentionCount +
|
||||
b.tracking.watchedThreadsUnreadCount;
|
||||
|
||||
if (aUrgent > 0 || bUrgent > 0) {
|
||||
return aUrgent > bUrgent ? -1 : 1;
|
||||
}
|
||||
|
||||
if (
|
||||
a.threadsManager.unreadThreadCount > 0 ||
|
||||
b.threadsManager.unreadThreadCount > 0
|
||||
a.unreadThreadsCountSinceLastViewed > 0 ||
|
||||
b.unreadThreadsCountSinceLastViewed > 0
|
||||
) {
|
||||
return a.threadsManager.unreadThreadCount >
|
||||
b.threadsManager.unreadThreadCount
|
||||
return a.unreadThreadsCountSinceLastViewed >
|
||||
b.unreadThreadsCountSinceLastViewed
|
||||
? -1
|
||||
: 1;
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ export default class ChatTrackingStateManager extends Service {
|
|||
|
||||
get allChannelUrgentCount() {
|
||||
return (
|
||||
this.publicChannelMentionCount +
|
||||
this.allChannelMentionCount +
|
||||
this.directMessageUnreadCount +
|
||||
this.watchedThreadsUnreadCount
|
||||
);
|
||||
|
|
|
@ -122,7 +122,7 @@ RSpec.describe "Mobile Chat footer", type: :system, mobile: true do
|
|||
|
||||
context "for direct messages" do
|
||||
fab!(:dm_channel) { Fabricate(:direct_message_channel, users: [current_user]) }
|
||||
fab!(:dm_message) { Fabricate(:chat_message, chat_channel: dm_channel) }
|
||||
fab!(:dm_message) { Fabricate(:chat_message, chat_channel: dm_channel, user: current_user) }
|
||||
|
||||
it "is urgent" do
|
||||
visit("/")
|
||||
|
@ -130,6 +130,40 @@ RSpec.describe "Mobile Chat footer", type: :system, mobile: true do
|
|||
|
||||
expect(page).to have_css("#c-footer-direct-messages .c-unread-indicator.-urgent")
|
||||
end
|
||||
|
||||
context "with threads" do
|
||||
fab!(:thread) { Fabricate(:chat_thread, channel: dm_channel, original_message: dm_message) }
|
||||
|
||||
before do
|
||||
SiteSetting.chat_threads_enabled = true
|
||||
dm_channel.membership_for(current_user).mark_read!(dm_message.id)
|
||||
end
|
||||
|
||||
it "is urgent for thread mentions" do
|
||||
Jobs.run_immediately!
|
||||
|
||||
thread.membership_for(current_user).update!(
|
||||
notification_level: ::Chat::NotificationLevels.all[:normal],
|
||||
)
|
||||
|
||||
visit("/")
|
||||
chat_page.open_from_header
|
||||
|
||||
expect(page).to have_no_css("#c-footer-direct-messages .c-unread-indicator.-urgent")
|
||||
|
||||
Fabricate(
|
||||
:chat_message_with_service,
|
||||
chat_channel: dm_channel,
|
||||
thread: thread,
|
||||
message: "hello @#{current_user.username}",
|
||||
)
|
||||
|
||||
expect(page).to have_css(
|
||||
"#c-footer-direct-messages .c-unread-indicator.-urgent",
|
||||
text: "1",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "for my threads" do
|
||||
|
|
|
@ -177,6 +177,35 @@ RSpec.describe "Message notifications - mobile", type: :system, mobile: true do
|
|||
".chat-channel-row:nth-child(2)[data-chat-channel-id=\"#{dm_channel_1.id}\"]",
|
||||
)
|
||||
end
|
||||
|
||||
context "with threads" do
|
||||
fab!(:message) do
|
||||
Fabricate(:chat_message, chat_channel: dm_channel_1, user: current_user)
|
||||
end
|
||||
fab!(:thread) do
|
||||
Fabricate(:chat_thread, channel: dm_channel_1, original_message: message)
|
||||
end
|
||||
|
||||
before { dm_channel_1.membership_for(current_user).mark_read!(message.id) }
|
||||
|
||||
it "shows urgent badge for mentions" do
|
||||
Jobs.run_immediately!
|
||||
|
||||
visit("/chat/direct-messages")
|
||||
|
||||
expect(channels_index_page).to have_no_unread_channel(dm_channel_1)
|
||||
|
||||
Fabricate(
|
||||
:chat_message_with_service,
|
||||
chat_channel: dm_channel_1,
|
||||
thread: thread,
|
||||
message: "hello @#{current_user.username}",
|
||||
user: user_1,
|
||||
)
|
||||
|
||||
expect(channels_index_page).to have_unread_channel(dm_channel_1, urgent: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ RSpec.describe "Message notifications - with sidebar", type: :system do
|
|||
let!(:chat_page) { PageObjects::Pages::Chat.new }
|
||||
let!(:channel_page) { PageObjects::Pages::ChatChannel.new }
|
||||
let!(:thread_page) { PageObjects::Pages::ChatThread.new }
|
||||
let!(:sidebar) { PageObjects::Pages::Sidebar.new }
|
||||
|
||||
before do
|
||||
SiteSetting.navigation_menu = "sidebar"
|
||||
|
@ -233,36 +234,75 @@ RSpec.describe "Message notifications - with sidebar", type: :system do
|
|||
end
|
||||
end
|
||||
|
||||
context "with a thread" do
|
||||
fab!(:channel) { Fabricate(:category_channel, threading_enabled: true) }
|
||||
context "with threads" do
|
||||
fab!(:other_user) { Fabricate(:user) }
|
||||
fab!(:thread) do
|
||||
chat_thread_chain_bootstrap(channel: channel, users: [current_user, other_user])
|
||||
end
|
||||
|
||||
before do
|
||||
channel.membership_for(current_user).mark_read!
|
||||
thread.membership_for(current_user).mark_read!
|
||||
|
||||
visit("/")
|
||||
end
|
||||
|
||||
context "when chat_header_indicator_preference is 'all_new'" do
|
||||
before do
|
||||
current_user.user_option.update!(
|
||||
chat_header_indicator_preference:
|
||||
UserOption.chat_header_indicator_preferences[:all_new],
|
||||
)
|
||||
context "with public channels" do
|
||||
fab!(:channel) { Fabricate(:category_channel, threading_enabled: true) }
|
||||
fab!(:thread) do
|
||||
chat_thread_chain_bootstrap(channel: channel, users: [current_user, other_user])
|
||||
end
|
||||
|
||||
context "when a reply is created" do
|
||||
it "shows the unread indicator in the header" do
|
||||
expect(page).to have_no_css(".chat-header-icon .chat-channel-unread-indicator")
|
||||
before do
|
||||
channel.membership_for(current_user).mark_read!
|
||||
thread.membership_for(current_user).mark_read!
|
||||
|
||||
create_message(thread: thread, creator: other_user)
|
||||
visit("/")
|
||||
end
|
||||
|
||||
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator")
|
||||
end
|
||||
it "shows the unread badge in chat header" do
|
||||
expect(page).to have_no_css(".chat-header-icon .chat-channel-unread-indicator")
|
||||
|
||||
create_message(thread: thread, creator: other_user, text: "this is a test")
|
||||
|
||||
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator")
|
||||
end
|
||||
end
|
||||
|
||||
context "with direct message channels" do
|
||||
fab!(:dm_channel) do
|
||||
Fabricate(:direct_message_channel, users: [current_user, other_user])
|
||||
end
|
||||
fab!(:thread) do
|
||||
chat_thread_chain_bootstrap(channel: dm_channel, users: [current_user, other_user])
|
||||
end
|
||||
|
||||
before do
|
||||
dm_channel.membership_for(current_user).mark_read!
|
||||
thread.membership_for(current_user).mark_read!
|
||||
|
||||
visit("/")
|
||||
end
|
||||
|
||||
it "shows the unread indicator in the sidebar for tracked threads" do
|
||||
expect(page).to have_no_css(".sidebar-row.channel-#{dm_channel.id} .unread")
|
||||
|
||||
create_message(channel: dm_channel, thread: thread, creator: other_user)
|
||||
|
||||
expect(page).to have_css(".sidebar-row.channel-#{dm_channel.id} .unread")
|
||||
end
|
||||
|
||||
it "shows the urgent indicator in the sidebar for tracked threads" do
|
||||
expect(page).to have_no_css(".sidebar-row.channel-#{dm_channel.id} .urgent")
|
||||
|
||||
thread.membership_for(current_user).update!(notification_level: :watching)
|
||||
|
||||
create_message(channel: dm_channel, thread: thread, creator: other_user)
|
||||
|
||||
expect(page).to have_css(".sidebar-row.channel-#{dm_channel.id} .urgent")
|
||||
end
|
||||
|
||||
it "shows the urgent indicator in the chat sidebar for mentions" do
|
||||
expect(page).to have_no_css(".sidebar-row.channel-#{dm_channel.id} .urgent")
|
||||
|
||||
create_message(
|
||||
channel: dm_channel,
|
||||
thread: thread,
|
||||
creator: other_user,
|
||||
text: "hey @#{current_user.username}",
|
||||
)
|
||||
|
||||
expect(page).to have_css(".sidebar-row.channel-#{dm_channel.id} .urgent")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue