FEATURE: Add new chat indicator preference for Only Mentions (#23848)

Add new chat indicator preference within chat user preferences.

Enabling this option will mean that green notifications will only appear for mentions (within channels and DMs.

This change also enables mentions within direct messages.
This commit is contained in:
David Battersby 2023-10-27 15:58:19 +08:00 committed by GitHub
parent f9f9cf0bf4
commit 8465324168
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 125 additions and 15 deletions

View File

@ -1,9 +1,11 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import { hasChatIndicator } from "../lib/chat-user-preferences";
export default class ChatChannelUnreadIndicator extends Component { export default class ChatChannelUnreadIndicator extends Component {
@service chat; @service chat;
@service site; @service site;
@service currentUser;
get showUnreadIndicator() { get showUnreadIndicator() {
return ( return (
@ -16,17 +18,33 @@ export default class ChatChannelUnreadIndicator extends Component {
} }
get unreadCount() { get unreadCount() {
if (this.#onlyMentions() && this.#hasChannelMentions()) {
return this.args.channel.tracking.mentionCount;
}
return this.args.channel.tracking.unreadCount; return this.args.channel.tracking.unreadCount;
} }
get isUrgent() { get isUrgent() {
if (this.#onlyMentions()) {
return this.#hasChannelMentions();
}
return ( return (
this.args.channel.isDirectMessageChannel || this.args.channel.isDirectMessageChannel || this.#hasChannelMentions()
this.args.channel.tracking.mentionCount > 0
); );
} }
get showUnreadCount() { get showUnreadCount() {
if (this.#onlyMentions()) {
return this.#hasChannelMentions();
}
return this.args.channel.isDirectMessageChannel || this.isUrgent; return this.args.channel.isDirectMessageChannel || this.isUrgent;
} }
#hasChannelMentions() {
return this.args.channel.tracking.mentionCount > 0;
}
#onlyMentions() {
return hasChatIndicator(this.currentUser).ONLY_MENTIONS;
}
} }

View File

@ -4,6 +4,7 @@ import {
HEADER_INDICATOR_PREFERENCE_ALL_NEW, HEADER_INDICATOR_PREFERENCE_ALL_NEW,
HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS, HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS,
HEADER_INDICATOR_PREFERENCE_NEVER, HEADER_INDICATOR_PREFERENCE_NEVER,
HEADER_INDICATOR_PREFERENCE_ONLY_MENTIONS,
} from "discourse/plugins/chat/discourse/controllers/preferences-chat"; } from "discourse/plugins/chat/discourse/controllers/preferences-chat";
const MAX_UNREAD_COUNT = 99; const MAX_UNREAD_COUNT = 99;
@ -19,6 +20,13 @@ export default class ChatHeaderIconUnreadIndicator extends Component {
); );
} }
get mentionCount() {
return (
this.args.mentionCount ||
this.chatTrackingStateManager.allChannelMentionCount
);
}
get unreadCount() { get unreadCount() {
return ( return (
this.args.unreadCount || this.args.unreadCount ||
@ -34,6 +42,10 @@ export default class ChatHeaderIconUnreadIndicator extends Component {
} }
get showUrgentIndicator() { get showUrgentIndicator() {
if (this.onlyMentions) {
return this.mentionCount > 0;
}
return ( return (
this.urgentCount > 0 && this.urgentCount > 0 &&
this.#hasAnyIndicatorPreference([ this.#hasAnyIndicatorPreference([
@ -50,10 +62,15 @@ export default class ChatHeaderIconUnreadIndicator extends Component {
); );
} }
get unreadCountLabel() { get urgentCountLabel() {
return this.urgentCount > MAX_UNREAD_COUNT let totalCount = this.onlyMentions ? this.mentionCount : this.urgentCount;
? `${MAX_UNREAD_COUNT}+` return totalCount > MAX_UNREAD_COUNT ? `${MAX_UNREAD_COUNT}+` : totalCount;
: this.urgentCount; }
get onlyMentions() {
return this.#hasAnyIndicatorPreference([
HEADER_INDICATOR_PREFERENCE_ONLY_MENTIONS,
]);
} }
#hasAnyIndicatorPreference(preferences) { #hasAnyIndicatorPreference(preferences) {
@ -71,7 +88,7 @@ export default class ChatHeaderIconUnreadIndicator extends Component {
{{#if this.showUrgentIndicator}} {{#if this.showUrgentIndicator}}
<div class="chat-channel-unread-indicator -urgent"> <div class="chat-channel-unread-indicator -urgent">
<div class="chat-channel-unread-indicator__number"> <div class="chat-channel-unread-indicator__number">
{{this.unreadCountLabel}} {{this.urgentCountLabel}}
</div> </div>
</div> </div>
{{else if this.showUnreadIndicator}} {{else if this.showUnreadIndicator}}

View File

@ -5,6 +5,7 @@ import {
HEADER_INDICATOR_PREFERENCE_ALL_NEW, HEADER_INDICATOR_PREFERENCE_ALL_NEW,
HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS, HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS,
HEADER_INDICATOR_PREFERENCE_NEVER, HEADER_INDICATOR_PREFERENCE_NEVER,
HEADER_INDICATOR_PREFERENCE_ONLY_MENTIONS,
} from "discourse/plugins/chat/discourse/controllers/preferences-chat"; } from "discourse/plugins/chat/discourse/controllers/preferences-chat";
export default class ChatStyleguideChatHeaderIcon extends Component { export default class ChatStyleguideChatHeaderIcon extends Component {
@ -18,6 +19,7 @@ export default class ChatStyleguideChatHeaderIcon extends Component {
return [ return [
HEADER_INDICATOR_PREFERENCE_ALL_NEW, HEADER_INDICATOR_PREFERENCE_ALL_NEW,
HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS, HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS,
HEADER_INDICATOR_PREFERENCE_ONLY_MENTIONS,
HEADER_INDICATOR_PREFERENCE_NEVER, HEADER_INDICATOR_PREFERENCE_NEVER,
]; ];
} }

View File

@ -25,6 +25,7 @@ const EMAIL_FREQUENCY_OPTIONS = [
export const HEADER_INDICATOR_PREFERENCE_NEVER = "never"; export const HEADER_INDICATOR_PREFERENCE_NEVER = "never";
export const HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS = "dm_and_mentions"; export const HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS = "dm_and_mentions";
export const HEADER_INDICATOR_PREFERENCE_ALL_NEW = "all_new"; export const HEADER_INDICATOR_PREFERENCE_ALL_NEW = "all_new";
export const HEADER_INDICATOR_PREFERENCE_ONLY_MENTIONS = "only_mentions";
const HEADER_INDICATOR_OPTIONS = [ const HEADER_INDICATOR_OPTIONS = [
{ {
name: I18n.t("chat.header_indicator_preference.all_new"), name: I18n.t("chat.header_indicator_preference.all_new"),
@ -34,6 +35,10 @@ const HEADER_INDICATOR_OPTIONS = [
name: I18n.t("chat.header_indicator_preference.dm_and_mentions"), name: I18n.t("chat.header_indicator_preference.dm_and_mentions"),
value: HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS, value: HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS,
}, },
{
name: I18n.t("chat.header_indicator_preference.only_mentions"),
value: HEADER_INDICATOR_PREFERENCE_ONLY_MENTIONS,
},
{ {
name: I18n.t("chat.header_indicator_preference.never"), name: I18n.t("chat.header_indicator_preference.never"),
value: HEADER_INDICATOR_PREFERENCE_NEVER, value: HEADER_INDICATOR_PREFERENCE_NEVER,

View File

@ -0,0 +1,17 @@
import {
HEADER_INDICATOR_PREFERENCE_ALL_NEW,
HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS,
HEADER_INDICATOR_PREFERENCE_NEVER,
HEADER_INDICATOR_PREFERENCE_ONLY_MENTIONS,
} from "discourse/plugins/chat/discourse/controllers/preferences-chat";
export function hasChatIndicator(user) {
const pref = user.user_option.chat_header_indicator_preference;
return {
ALL_NEW: pref === HEADER_INDICATOR_PREFERENCE_ALL_NEW,
DM_AND_MENTIONS: pref === HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS,
ONLY_MENTIONS: pref === HEADER_INDICATOR_PREFERENCE_ONLY_MENTIONS,
NEVER: pref === HEADER_INDICATOR_PREFERENCE_NEVER,
};
}

View File

@ -25,9 +25,9 @@ export default class ChatSubscriptionsManager extends Service {
} }
this._channelSubscriptions.add(channel.id); this._channelSubscriptions.add(channel.id);
this._startChannelMentionsSubscription(channel);
if (!channel.isDirectMessageChannel) { if (!channel.isDirectMessageChannel) {
this._startChannelMentionsSubscription(channel);
this._startKickFromChannelSubscription(channel); this._startKickFromChannelSubscription(channel);
} }

View File

@ -42,20 +42,38 @@ export default class ChatTrackingStateManager extends Service {
} }
get publicChannelUnreadCount() { get publicChannelUnreadCount() {
return this.#publicChannels().reduce((unreadCount, channel) => { return this.#publicChannels.reduce((unreadCount, channel) => {
return unreadCount + channel.tracking.unreadCount; return unreadCount + channel.tracking.unreadCount;
}, 0); }, 0);
} }
get allChannelMentionCount() {
let totalPublicMentions = this.#publicChannels.reduce(
(channelMentionCount, channel) => {
return channelMentionCount + channel.tracking.mentionCount;
},
0
);
let totalPrivateMentions = this.#directMessageChannels.reduce(
(dmMentionCount, channel) => {
return dmMentionCount + channel.tracking.mentionCount;
},
0
);
return totalPublicMentions + totalPrivateMentions;
}
get allChannelUrgentCount() { get allChannelUrgentCount() {
let publicChannelMentionCount = this.#publicChannels().reduce( let publicChannelMentionCount = this.#publicChannels.reduce(
(mentionCount, channel) => { (mentionCount, channel) => {
return mentionCount + channel.tracking.mentionCount; return mentionCount + channel.tracking.mentionCount;
}, },
0 0
); );
let dmChannelUnreadCount = this.#directMessageChannels().reduce( let dmChannelUnreadCount = this.#directMessageChannels.reduce(
(unreadCount, channel) => { (unreadCount, channel) => {
return unreadCount + channel.tracking.unreadCount; return unreadCount + channel.tracking.unreadCount;
}, },
@ -96,11 +114,11 @@ export default class ChatTrackingStateManager extends Service {
model.tracking.mentionCount = state.mention_count; model.tracking.mentionCount = state.mention_count;
} }
#publicChannels() { get #publicChannels() {
return this.chatChannelsManager.publicMessageChannels; return this.chatChannelsManager.publicMessageChannels;
} }
#directMessageChannels() { get #directMessageChannels() {
return this.chatChannelsManager.directMessageChannels; return this.chatChannelsManager.directMessageChannels;
} }
} }

View File

@ -118,6 +118,7 @@ en:
title: "Show activity indicator in header" title: "Show activity indicator in header"
all_new: "All New Messages" all_new: "All New Messages"
dm_and_mentions: "Direct Messages and Mentions" dm_and_mentions: "Direct Messages and Mentions"
only_mentions: "Only Mentions"
never: "Never" never: "Never"
separate_sidebar_mode: separate_sidebar_mode:
title: "Show separate sidebar modes for forum and chat" title: "Show separate sidebar modes for forum and chat"

View File

@ -20,7 +20,12 @@ module Chat
end end
def base.chat_header_indicator_preferences def base.chat_header_indicator_preferences
@chat_header_indicator_preferences ||= { all_new: 0, dm_and_mentions: 1, never: 2 } @chat_header_indicator_preferences ||= {
all_new: 0,
dm_and_mentions: 1,
never: 2,
only_mentions: 3,
}
end end
# Avoid attempting to override when autoloading # Avoid attempting to override when autoloading

View File

@ -12,7 +12,7 @@ RSpec.describe "Message notifications - with sidebar", type: :system do
end end
def create_message(text: nil, channel:, creator: Fabricate(:user)) def create_message(text: nil, channel:, creator: Fabricate(:user))
Fabricate(:chat_message_with_service, chat_channel: channel, user: creator, message: text) Fabricate(:chat_message_with_service, chat_channel: channel, message: text, user: creator)
end end
context "as a user" do context "as a user" do
@ -165,6 +165,33 @@ RSpec.describe "Message notifications - with sidebar", type: :system do
fab!(:dm_channel_1) { Fabricate(:direct_message_channel, users: [current_user, user_1]) } fab!(:dm_channel_1) { Fabricate(:direct_message_channel, users: [current_user, user_1]) }
fab!(:dm_channel_2) { Fabricate(:direct_message_channel, users: [current_user, user_2]) } fab!(:dm_channel_2) { Fabricate(:direct_message_channel, users: [current_user, user_2]) }
context "when chat_header_indicator_preference is 'only_mentions'" do
before do
current_user.user_option.update!(
chat_header_indicator_preference:
UserOption.chat_header_indicator_preferences[:only_mentions],
)
end
it "doesn't show indicator on chat-header-icon for messages" do
visit("/")
create_message(channel: dm_channel_1, creator: user_1)
expect(page).to have_no_css(".chat-header-icon .chat-channel-unread-indicator.-urgent")
end
it "does show an indicator on chat-header-icon for mentions" do
Jobs.run_immediately!
visit("/")
create_message(
text: "hey what's up @#{current_user.username}?",
channel: dm_channel_1,
creator: user_1,
)
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator.-urgent")
end
end
context "when a message is created" do context "when a message is created" do
it "correctly renders notifications" do it "correctly renders notifications" do
visit("/") visit("/")