FEATURE: Chat header icon indicator preference (#20474)

This commit allows the user to set their preference vis-a-vis
the chat icon in the header of the page. There are three options:

- All New (default) - This maintains the existing behaviour where
  all new messages in the channel show a blue dot on the icon
- Direct Messages and Mentions - Only show the green dot on the
  icon when you are directly messaged or mentioned, the blue dot
  is never shown
- Never - Never show any dot on the chat icon, for those who
  want tractor-beam-laser-focus
This commit is contained in:
Martin Brennan 2023-03-01 11:01:44 +10:00 committed by GitHub
parent c051992098
commit d3a1b09361
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 170 additions and 2 deletions

View File

@ -292,6 +292,7 @@ end
# enable_experimental_sidebar :boolean default(FALSE)
# seen_popups :integer is an Array
# sidebar_list_destination :integer default("none_selected"), not null
# chat_header_indicator_preference :integer default(0), not null
#
# Indexes
#

View File

@ -1,9 +1,9 @@
{{#if (gt this.chatChannelsManager.unreadUrgentCount 0)}}
{{#if this.showUrgentIndicator}}
<div class="chat-channel-unread-indicator urgent">
<div class="number-wrap">
<div class="number">{{this.chatChannelsManager.unreadUrgentCount}}</div>
</div>
</div>
{{else if (gt this.chatChannelsManager.unreadCount 0)}}
{{else if this.showUnreadIndicator}}
<div class="chat-channel-unread-indicator"></div>
{{/if}}

View File

@ -1,6 +1,44 @@
import { inject as service } from "@ember/service";
import Component from "@glimmer/component";
import {
HEADER_INDICATOR_PREFERENCE_ALL_NEW,
HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS,
HEADER_INDICATOR_PREFERENCE_NEVER,
} from "../controllers/preferences-chat";
export default class ChatHeaderIconUnreadIndicator extends Component {
@service chatChannelsManager;
@service currentUser;
get showUrgentIndicator() {
return (
this.chatChannelsManager.unreadUrgentCount > 0 &&
this.#hasAnyIndicatorPreference([
HEADER_INDICATOR_PREFERENCE_ALL_NEW,
HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS,
])
);
}
get showUnreadIndicator() {
return (
this.chatChannelsManager.unreadCount > 0 &&
this.#hasAnyIndicatorPreference([HEADER_INDICATOR_PREFERENCE_ALL_NEW])
);
}
get indicatorPreference() {
return this.currentUser.user_option.chat_header_indicator_preference;
}
#hasAnyIndicatorPreference(preferences) {
if (
!this.currentUser ||
this.indicatorPreference === HEADER_INDICATOR_PREFERENCE_NEVER
) {
return false;
}
return preferences.includes(this.indicatorPreference);
}
}

View File

@ -13,18 +13,39 @@ const CHAT_ATTRS = [
"ignore_channel_wide_mention",
"chat_sound",
"chat_email_frequency",
"chat_header_indicator_preference",
];
export const HEADER_INDICATOR_PREFERENCE_NEVER = "never";
export const HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS = "dm_and_mentions";
export const HEADER_INDICATOR_PREFERENCE_ALL_NEW = "all_new";
const EMAIL_FREQUENCY_OPTIONS = [
{ name: I18n.t(`chat.email_frequency.never`), value: "never" },
{ name: I18n.t(`chat.email_frequency.when_away`), value: "when_away" },
];
const HEADER_INDICATOR_OPTIONS = [
{
name: I18n.t(`chat.header_indicator_preference.all_new`),
value: HEADER_INDICATOR_PREFERENCE_ALL_NEW,
},
{
name: I18n.t(`chat.header_indicator_preference.dm_and_mentions`),
value: HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS,
},
{
name: I18n.t(`chat.header_indicator_preference.never`),
value: HEADER_INDICATOR_PREFERENCE_NEVER,
},
];
export default class PreferencesChatController extends Controller {
@service chatAudioManager;
subpageTitle = I18n.t("chat.admin.title");
emailFrequencyOptions = EMAIL_FREQUENCY_OPTIONS;
headerIndicatorOptions = HEADER_INDICATOR_OPTIONS;
@discourseComputed
chatSounds() {

View File

@ -5,6 +5,7 @@ const ONLY_CHAT_PUSH_NOTIFICATIONS_FIELD = "only_chat_push_notifications";
const IGNORE_CHANNEL_WIDE_MENTION = "ignore_channel_wide_mention";
const CHAT_SOUND = "chat_sound";
const CHAT_EMAIL_FREQUENCY = "chat_email_frequency";
const CHAT_HEADER_INDICATOR_PREFERENCE = "chat_header_indicator_preference";
export default {
name: "chat-user-options",
@ -18,6 +19,7 @@ export default {
api.addSaveableUserOptionField(IGNORE_CHANNEL_WIDE_MENTION);
api.addSaveableUserOptionField(CHAT_SOUND);
api.addSaveableUserOptionField(CHAT_EMAIL_FREQUENCY);
api.addSaveableUserOptionField(CHAT_HEADER_INDICATOR_PREFERENCE);
}
});
},

View File

@ -69,6 +69,21 @@
{{/if}}
</div>
<div class="control-group chat-setting controls-dropdown">
<label for="user_chat_header_indicator_preference">
{{i18n "chat.header_indicator_preference.title"}}
</label>
<ComboBox
@valueProperty="value"
@content={{this.headerIndicatorOptions}}
@value={{this.model.user_option.chat_header_indicator_preference}}
@id="user_chat_header_indicator_preference"
@onChange={{action
(mut this.model.user_option.chat_header_indicator_preference)
}}
/>
</div>
<SaveControls
@id="user_chat_preference_save"
@model={{this.model}}

View File

@ -94,6 +94,11 @@ en:
never: "Never"
title: "Email Notifications"
when_away: "Only when away"
header_indicator_preference:
title: "Show activity indicator in header"
all_new: "All New Messages"
dm_and_mentions: "Direct Messages and Mentions"
never: "Never"
enable: "Enable chat"
flag: "Flag"
emoji: "Insert emoji"

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddChatHeaderIndicatorPreference < ActiveRecord::Migration[7.0]
def change
add_column :user_options, :chat_header_indicator_preference, :integer, default: 0, null: false
end
end

View File

@ -13,6 +13,11 @@ module Chat::UserOptionExtension
@chat_email_frequencies ||= { never: 0, when_away: 1 }
end
def base.chat_header_indicator_preferences
@chat_header_indicator_preferences ||= { all_new: 0, dm_and_mentions: 1, never: 2 }
end
base.enum :chat_email_frequency, base.chat_email_frequencies, prefix: "send_chat_email"
base.enum :chat_header_indicator_preference, base.chat_header_indicator_preferences
end
end

View File

@ -264,6 +264,7 @@ after_initialize do
UserUpdater::OPTION_ATTR.push(:chat_sound)
UserUpdater::OPTION_ATTR.push(:ignore_channel_wide_mention)
UserUpdater::OPTION_ATTR.push(:chat_email_frequency)
UserUpdater::OPTION_ATTR.push(:chat_header_indicator_preference)
register_reviewable_type ReviewableChatMessage
@ -475,6 +476,14 @@ after_initialize do
add_to_serializer(:user_option, :chat_email_frequency) { object.chat_email_frequency }
add_to_serializer(:user_option, :chat_header_indicator_preference) do
object.chat_header_indicator_preference
end
add_to_serializer(:current_user_option, :chat_header_indicator_preference) do
object.chat_header_indicator_preference
end
RETENTION_SETTINGS_TO_USER_OPTION_FIELDS = {
chat_channel_retention_days: :dismissed_channel_retention_reminder,
chat_dm_retention_days: :dismissed_dm_retention_reminder,

View File

@ -79,6 +79,60 @@ RSpec.describe "Message notifications - with sidebar", type: :system, js: true d
end
end
context "when user chat_header_indicator_preference is set to 'never'" do
before do
current_user.user_option.update!(
chat_header_indicator_preference:
UserOption.chat_header_indicator_preferences[:never],
)
end
context "when a message is created" do
it "doesn't show any indicator on chat-header-icon" do
visit("/")
using_session(:user_1) { create_message(channel: channel_1, creator: user_1) }
expect(page).to have_no_css(".chat-header-icon .chat-channel-unread-indicator")
end
end
end
context "when user chat_header_indicator_preference is set to 'dm_and_mentions'" do
before do
current_user.user_option.update!(
chat_header_indicator_preference:
UserOption.chat_header_indicator_preferences[:dm_and_mentions],
)
end
context "when a message is created" do
it "doesn't show any indicator on chat-header-icon" do
visit("/")
using_session(:user_1) { create_message(channel: channel_1, creator: user_1) }
expect(page).to have_no_css(
".chat-header-icon .chat-channel-unread-indicator.urgent",
)
end
end
context "when a message with a mention is created" do
it "does show an indicator on chat-header-icon" do
Jobs.run_immediately!
visit("/")
using_session(:user_1) do
create_message(
text: "hey what's going on @#{current_user.username}?",
channel: channel_1,
creator: user_1,
)
end
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator.urgent")
end
end
end
context "when a message is created" do
it "correctly renders notifications" do
visit("/")

View File

@ -32,6 +32,17 @@ RSpec.describe "User chat preferences", type: :system, js: true do
expect(page).to have_css("#user_chat_sounds .select-kit-header[data-value='bell']")
end
it "can select header_indicator_preference" do
visit("/u/#{current_user.username}/preferences/chat")
find("#user_chat_header_indicator_preference .select-kit-header[data-value]").click
find("[data-value='dm_and_mentions']").click
find(".save-changes").click
expect(page).to have_css(
"#user_chat_header_indicator_preference .select-kit-header[data-value='dm_and_mentions']",
)
end
context "as an admin on another user's preferences" do
fab!(:current_user) { Fabricate(:admin) }
fab!(:user_1) { Fabricate(:user) }