FEATURE: implements user based sidebar mode (#23078)

This commit is contained in:
Joffrey JAFFEUX 2023-08-18 20:33:07 +02:00 committed by GitHub
parent 82b16f4f47
commit b2b84cc957
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 602 additions and 52 deletions

View File

@ -5,5 +5,6 @@
@icon={{button.switchButtonIcon}} @icon={{button.switchButtonIcon}}
@disabled={{this.isSwitching}} @disabled={{this.isSwitching}}
@translatedLabel={{button.switchButtonLabel}} @translatedLabel={{button.switchButtonLabel}}
data-key={{button.key}}
/> />
{{/each}} {{/each}}

View File

@ -16,9 +16,13 @@ export default class SwitchPanelButtons extends Component {
const url = panel.lastKnownURL || panel.switchButtonDefaultUrl; const url = panel.lastKnownURL || panel.switchButtonDefaultUrl;
const destination = url === "/" ? `discovery.${defaultHomepage()}` : url; const destination = url === "/" ? `discovery.${defaultHomepage()}` : url;
this.router.transitionTo(destination).finally(() => { this.router
this.isSwitching = false; .transitionTo(destination)
this.sidebarState.setPanel(panel.key); .then(() => {
}); this.sidebarState.setPanel(panel.key);
})
.finally(() => {
this.isSwitching = false;
});
} }
} }

View File

@ -292,6 +292,7 @@ end
# sidebar_link_to_filtered_list :boolean default(FALSE), not null # sidebar_link_to_filtered_list :boolean default(FALSE), not null
# sidebar_show_count_of_new_items :boolean default(FALSE), not null # sidebar_show_count_of_new_items :boolean default(FALSE), not null
# watched_precedence_over_muted :boolean # watched_precedence_over_muted :boolean
# chat_separate_sidebar_mode :integer default(0), not null
# #
# Indexes # Indexes
# #

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
module Chat
class SeparateSidebarModeSiteSetting < EnumSiteSetting
def self.valid_value?(val)
values.any? { |v| v[:value] == val }
end
def self.values
@values ||= [
{ name: "admin.site_settings.chat_separate_sidebar_mode.never", value: "never" },
{ name: "admin.site_settings.chat_separate_sidebar_mode.always", value: "always" },
{ name: "admin.site_settings.chat_separate_sidebar_mode.fullscreen", value: "fullscreen" },
]
end
def self.translate_names?
true
end
end
end

View File

@ -171,8 +171,8 @@ export default Component.extend({
@action @action
openURL(url = null) { openURL(url = null) {
this.chat.activeChannel = null; this.chat.activeChannel = null;
this.chatStateManager.didOpenDrawer(url);
this.chatDrawerRouter.stateFor(this._routeFromURL(url)); this.chatDrawerRouter.stateFor(this._routeFromURL(url));
this.chatStateManager.didOpenDrawer(url);
}, },
_routeFromURL(url) { _routeFromURL(url) {

View File

@ -2,8 +2,9 @@
href={{this.href}} href={{this.href}}
tabindex="0" tabindex="0"
class={{concat-class "icon" "btn-flat" (if this.isActive "active")}} class={{concat-class "icon" "btn-flat" (if this.isActive "active")}}
title={{i18n this.title}}
> >
{{d-icon "d-chat"}} {{d-icon this.icon}}
{{#unless this.currentUserInDnD}} {{#unless this.currentUserInDnD}}
<Chat::Header::Icon::UnreadIndicator <Chat::Header::Icon::UnreadIndicator
@urgentCount={{@urgentCount}} @urgentCount={{@urgentCount}}

View File

@ -1,7 +1,7 @@
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import getURL from "discourse-common/lib/get-url"; import getURL from "discourse-common/lib/get-url";
import { getUserChatSeparateSidebarMode } from "discourse/plugins/chat/discourse/lib/get-user-chat-separate-sidebar-mode";
export default class ChatHeaderIcon extends Component { export default class ChatHeaderIcon extends Component {
@service currentUser; @service currentUser;
@service site; @service site;
@ -12,6 +12,10 @@ export default class ChatHeaderIcon extends Component {
return this.args.currentUserInDnD || this.currentUser.isInDoNotDisturb(); return this.args.currentUserInDnD || this.currentUser.isInDoNotDisturb();
} }
get chatSeparateSidebarMode() {
return getUserChatSeparateSidebarMode(this.currentUser);
}
get isActive() { get isActive() {
return ( return (
this.args.isActive || this.args.isActive ||
@ -20,13 +24,38 @@ export default class ChatHeaderIcon extends Component {
); );
} }
get title() {
if (
this.chatStateManager.isFullPageActive &&
!this.chatSeparateSidebarMode.never
) {
return "sidebar.panels.forum.label";
}
return "chat.title_capitalized";
}
get icon() {
if (
this.chatStateManager.isFullPageActive &&
!this.chatSeparateSidebarMode.never
) {
return "random";
}
return "d-chat";
}
get href() { get href() {
if (this.chatStateManager.isFullPageActive) { if (this.site.mobileView && this.chatStateManager.isFullPageActive) {
if (this.site.mobileView) { return getURL("/chat");
return getURL("/chat"); }
} else {
return getURL(this.router.currentURL); if (
} this.chatStateManager.isFullPageActive &&
!this.chatSeparateSidebarMode.never
) {
return getURL(this.chatStateManager.lastKnownAppURL || "/");
} }
if (this.chatStateManager.isDrawerActive) { if (this.chatStateManager.isDrawerActive) {

View File

@ -14,38 +14,65 @@ const CHAT_ATTRS = [
"chat_sound", "chat_sound",
"chat_email_frequency", "chat_email_frequency",
"chat_header_indicator_preference", "chat_header_indicator_preference",
"chat_separate_sidebar_mode",
];
const EMAIL_FREQUENCY_OPTIONS = [
{ name: I18n.t("chat.email_frequency.never"), value: "never" },
{ name: I18n.t("chat.email_frequency.when_away"), value: "when_away" },
]; ];
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";
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 = [ const HEADER_INDICATOR_OPTIONS = [
{ {
name: I18n.t(`chat.header_indicator_preference.all_new`), name: I18n.t("chat.header_indicator_preference.all_new"),
value: HEADER_INDICATOR_PREFERENCE_ALL_NEW, value: HEADER_INDICATOR_PREFERENCE_ALL_NEW,
}, },
{ {
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.never`), name: I18n.t("chat.header_indicator_preference.never"),
value: HEADER_INDICATOR_PREFERENCE_NEVER, value: HEADER_INDICATOR_PREFERENCE_NEVER,
}, },
]; ];
const CHAT_SEPARATE_SIDEBAR_MODE_OPTIONS = [
{
name: I18n.t("admin.site_settings.chat_separate_sidebar_mode.always"),
value: "always",
},
{
name: I18n.t("admin.site_settings.chat_separate_sidebar_mode.fullscreen"),
value: "fullscreen",
},
{
name: I18n.t("admin.site_settings.chat_separate_sidebar_mode.never"),
value: "never",
},
];
export default class PreferencesChatController extends Controller { export default class PreferencesChatController extends Controller {
@service chatAudioManager; @service chatAudioManager;
@service siteSettings;
subpageTitle = I18n.t("chat.admin.title"); subpageTitle = I18n.t("chat.admin.title");
emailFrequencyOptions = EMAIL_FREQUENCY_OPTIONS; emailFrequencyOptions = EMAIL_FREQUENCY_OPTIONS;
headerIndicatorOptions = HEADER_INDICATOR_OPTIONS; headerIndicatorOptions = HEADER_INDICATOR_OPTIONS;
chatSeparateSidebarModeOptions = CHAT_SEPARATE_SIDEBAR_MODE_OPTIONS;
get chatSeparateSidebarMode() {
const mode = this.model.get("user_option.chat_separate_sidebar_mode");
if (mode === "default") {
return this.siteSettings.chat_separate_sidebar_mode;
} else {
return mode;
}
}
@discourseComputed @discourseComputed
chatSounds() { chatSounds() {

View File

@ -11,6 +11,7 @@ import { decorateUsername } from "discourse/helpers/decorate-username-selector";
import { until } from "discourse/lib/formatter"; import { until } from "discourse/lib/formatter";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import ChatModalNewMessage from "discourse/plugins/chat/discourse/components/chat/modal/new-message"; import ChatModalNewMessage from "discourse/plugins/chat/discourse/components/chat/modal/new-message";
import getURL from "discourse-common/lib/get-url";
export default { export default {
name: "chat-sidebar", name: "chat-sidebar",
@ -23,6 +24,18 @@ export default {
this.siteSettings = container.lookup("service:site-settings"); this.siteSettings = container.lookup("service:site-settings");
withPluginApi("1.8.0", (api) => {
api.addSidebarPanel(
(BaseCustomSidebarPanel) =>
class ChatSidebarPanel extends BaseCustomSidebarPanel {
key = "chat";
switchButtonLabel = I18n.t("sidebar.panels.chat.label");
switchButtonIcon = "d-chat";
switchButtonDefaultUrl = getURL("/chat");
}
);
});
withPluginApi("1.3.0", (api) => { withPluginApi("1.3.0", (api) => {
if (this.siteSettings.enable_public_channels) { if (this.siteSettings.enable_public_channels) {
api.addSidebarSection( api.addSidebarSection(
@ -180,7 +193,8 @@ export default {
}; };
return SidebarChatChannelsSection; return SidebarChatChannelsSection;
} },
"chat"
); );
} }
@ -414,7 +428,8 @@ export default {
}; };
return SidebarChatDirectMessagesSection; return SidebarChatDirectMessagesSection;
} },
"chat"
); );
}); });
}, },

View File

@ -6,6 +6,7 @@ const IGNORE_CHANNEL_WIDE_MENTION = "ignore_channel_wide_mention";
const CHAT_SOUND = "chat_sound"; const CHAT_SOUND = "chat_sound";
const CHAT_EMAIL_FREQUENCY = "chat_email_frequency"; const CHAT_EMAIL_FREQUENCY = "chat_email_frequency";
const CHAT_HEADER_INDICATOR_PREFERENCE = "chat_header_indicator_preference"; const CHAT_HEADER_INDICATOR_PREFERENCE = "chat_header_indicator_preference";
const CHAT_SEPARATE_SIDEBAR_MODE = "chat_separate_sidebar_mode";
export default { export default {
name: "chat-user-options", name: "chat-user-options",
@ -20,6 +21,7 @@ export default {
api.addSaveableUserOptionField(CHAT_SOUND); api.addSaveableUserOptionField(CHAT_SOUND);
api.addSaveableUserOptionField(CHAT_EMAIL_FREQUENCY); api.addSaveableUserOptionField(CHAT_EMAIL_FREQUENCY);
api.addSaveableUserOptionField(CHAT_HEADER_INDICATOR_PREFERENCE); api.addSaveableUserOptionField(CHAT_HEADER_INDICATOR_PREFERENCE);
api.addSaveableUserOptionField(CHAT_SEPARATE_SIDEBAR_MODE);
} }
}); });
}, },

View File

@ -0,0 +1,9 @@
export function getUserChatSeparateSidebarMode(user) {
const mode = user?.get("user_option.chat_separate_sidebar_mode");
return {
never: "never" === mode,
always: "always" === mode,
fullscreen: "fullscreen" === mode,
};
}

View File

@ -4,11 +4,13 @@ import { defaultHomepage } from "discourse/lib/utilities";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import { scrollTop } from "discourse/mixins/scroll-top"; import { scrollTop } from "discourse/mixins/scroll-top";
import { schedule } from "@ember/runloop"; import { schedule } from "@ember/runloop";
import { withPluginApi } from "discourse/lib/plugin-api";
import { getUserChatSeparateSidebarMode } from "discourse/plugins/chat/discourse/lib/get-user-chat-separate-sidebar-mode";
export default class ChatRoute extends DiscourseRoute { export default class ChatRoute extends DiscourseRoute {
@service chat; @service chat;
@service router; @service router;
@service chatStateManager; @service chatStateManager;
@service currentUser;
titleToken() { titleToken() {
return I18n.t("chat.title_capitalized"); return I18n.t("chat.title_capitalized");
@ -57,6 +59,16 @@ export default class ChatRoute extends DiscourseRoute {
} }
activate() { activate() {
withPluginApi("1.8.0", (api) => {
api.setSidebarPanel("chat");
if (getUserChatSeparateSidebarMode(this.currentUser).never) {
api.setCombinedSidebarMode();
api.hideSidebarSwitchPanelButtons();
} else {
api.setSeparatedSidebarMode();
}
});
this.chatStateManager.storeAppURL(); this.chatStateManager.storeAppURL();
this.chat.updatePresence(); this.chat.updatePresence();
@ -68,6 +80,23 @@ export default class ChatRoute extends DiscourseRoute {
} }
deactivate(transition) { deactivate(transition) {
withPluginApi("1.8.0", (api) => {
api.setSidebarPanel("main");
const chatSeparateSidebarMode = getUserChatSeparateSidebarMode(
this.currentUser
);
if (chatSeparateSidebarMode.fullscreen) {
api.setCombinedSidebarMode();
api.showSidebarSwitchPanelButtons();
} else if (chatSeparateSidebarMode.always) {
api.setSeparatedSidebarMode();
} else {
api.setCombinedSidebarMode();
api.hideSidebarSwitchPanelButtons();
}
});
if (transition) { if (transition) {
const url = this.router.urlFor(transition.from.name); const url = this.router.urlFor(transition.from.name);
this.chatStateManager.storeChatURL(url); this.chatStateManager.storeChatURL(url);

View File

@ -4,6 +4,8 @@ import { tracked } from "@glimmer/tracking";
import KeyValueStore from "discourse/lib/key-value-store"; import KeyValueStore from "discourse/lib/key-value-store";
import Site from "discourse/models/site"; import Site from "discourse/models/site";
import getURL from "discourse-common/lib/get-url"; import getURL from "discourse-common/lib/get-url";
import { getUserChatSeparateSidebarMode } from "discourse/plugins/chat/discourse/lib/get-user-chat-separate-sidebar-mode";
import { withPluginApi } from "discourse/lib/plugin-api";
const PREFERRED_MODE_KEY = "preferred_mode"; const PREFERRED_MODE_KEY = "preferred_mode";
const PREFERRED_MODE_STORE_NAMESPACE = "discourse_chat_"; const PREFERRED_MODE_STORE_NAMESPACE = "discourse_chat_";
@ -56,6 +58,16 @@ export default class ChatStateManager extends Service {
} }
didOpenDrawer(url = null) { didOpenDrawer(url = null) {
withPluginApi("1.8.0", (api) => {
if (getUserChatSeparateSidebarMode(this.currentUser).always) {
api.setSidebarPanel("main");
api.setSeparatedSidebarMode();
api.hideSidebarSwitchPanelButtons();
} else {
api.setCombinedSidebarMode();
}
});
this.isDrawerActive = true; this.isDrawerActive = true;
this.isDrawerExpanded = true; this.isDrawerExpanded = true;
@ -68,6 +80,24 @@ export default class ChatStateManager extends Service {
} }
didCloseDrawer() { didCloseDrawer() {
withPluginApi("1.8.0", (api) => {
api.setSidebarPanel("main");
const chatSeparateSidebarMode = getUserChatSeparateSidebarMode(
this.currentUser
);
if (chatSeparateSidebarMode.fullscreen) {
api.setCombinedSidebarMode();
api.showSidebarSwitchPanelButtons();
} else if (chatSeparateSidebarMode.always) {
api.setSeparatedSidebarMode();
api.showSidebarSwitchPanelButtons();
} else {
api.setCombinedSidebarMode();
api.hideSidebarSwitchPanelButtons();
}
});
this.isDrawerActive = false; this.isDrawerActive = false;
this.isDrawerExpanded = false; this.isDrawerExpanded = false;
this.chat.updatePresence(); this.chat.updatePresence();

View File

@ -99,6 +99,23 @@
/> />
</div> </div>
<div
class="control-group chat-setting controls-dropdown"
data-setting-name="user_chat_separate_sidebar_mode"
>
<label for="user_chat_separate_sidebar_mode">
{{i18n "chat.separate_sidebar_mode.title"}}
</label>
<ComboBox
@valueProperty="value"
@content={{this.chatSeparateSidebarModeOptions}}
@value={{this.chatSeparateSidebarMode}}
@id="user_chat_separate_sidebar_mode"
@onChange={{action (mut this.model.user_option.chat_separate_sidebar_mode)}}
/>
</div>
<SaveControls <SaveControls
@id="user_chat_preference_save" @id="user_chat_preference_save"
@model={{this.model}} @model={{this.model}}

View File

@ -4,7 +4,6 @@ import { hbs } from "ember-cli-htmlbars";
export default createWidget("chat-header-icon", { export default createWidget("chat-header-icon", {
tagName: "li.header-dropdown-toggle.chat-header-icon", tagName: "li.header-dropdown-toggle.chat-header-icon",
title: "chat.title_capitalized",
services: ["chat"], services: ["chat"],

View File

@ -4,6 +4,10 @@ en:
site_settings: site_settings:
categories: categories:
chat: Chat chat: Chat
chat_separate_sidebar_mode:
always: "Always"
fullscreen: "When chat is in fullscreen"
never: "Never"
logs: logs:
staff_actions: staff_actions:
actions: actions:
@ -104,6 +108,8 @@ en:
all_new: "All New Messages" all_new: "All New Messages"
dm_and_mentions: "Direct Messages and Mentions" dm_and_mentions: "Direct Messages and Mentions"
never: "Never" never: "Never"
separate_sidebar_mode:
title: "Show separate sidebar modes for forum and chat"
enable: "Enable chat" enable: "Enable chat"
flag: "Flag" flag: "Flag"
emoji: "Insert emoji" emoji: "Insert emoji"
@ -685,3 +691,8 @@ en:
sections: sections:
chat: chat:
title: Chat title: Chat
sidebar:
panels:
chat:
label: "Chat"

View File

@ -1,5 +1,6 @@
en: en:
site_settings: site_settings:
chat_separate_sidebar_mode: "Show separate sidebar modes for forum and chat."
chat_enabled: "Enable the chat plugin." chat_enabled: "Enable the chat plugin."
enable_public_channels: "Enable public channels based on categories." enable_public_channels: "Enable public channels based on categories."
chat_allowed_groups: "Users in these groups can chat. Note that staff can always access chat." chat_allowed_groups: "Users in these groups can chat. Note that staff can always access chat."

View File

@ -116,3 +116,8 @@ chat:
max_chat_draft_length: max_chat_draft_length:
default: 50_000 default: 50_000
hidden: true hidden: true
chat_separate_sidebar_mode:
client: true
default: "never"
type: enum
enum: "Chat::SeparateSidebarModeSiteSetting"

View File

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

View File

@ -14,19 +14,32 @@ module Chat
@chat_email_frequencies ||= { never: 0, when_away: 1 } @chat_email_frequencies ||= { never: 0, when_away: 1 }
end end
# Avoid attempting to override when autoloading
if !base.method_defined?(:send_chat_email_never?)
base.enum :chat_email_frequency, base.chat_email_frequencies, prefix: "send_chat_email"
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 }
end end
if !base.method_defined?(:send_chat_email_never?) # Avoid attempting to override when autoloading # Avoid attempting to override when autoloading
base.enum :chat_email_frequency, base.chat_email_frequencies, prefix: "send_chat_email" if !base.method_defined?(:chat_header_indicator_never?)
end
if !base.method_defined?(:chat_header_indicator_never?) # Avoid attempting to override when autoloading
base.enum :chat_header_indicator_preference, base.enum :chat_header_indicator_preference,
base.chat_header_indicator_preferences, base.chat_header_indicator_preferences,
prefix: "chat_header_indicator" prefix: "chat_header_indicator"
end end
def base.chat_separate_sidebar_mode
@chat_separate_sidebar_mode ||= { default: 0, never: 1, always: 2, fullscreen: 3 }
end
# Avoid attempting to override when autoloading
if !base.method_defined?(:chat_separate_sidebar_mode_default?)
base.enum :chat_separate_sidebar_mode,
base.chat_separate_sidebar_mode,
prefix: "chat_separate_sidebar_mode"
end
end end
end end
end end

View File

@ -47,6 +47,7 @@ after_initialize do
UserUpdater::OPTION_ATTR.push(:ignore_channel_wide_mention) UserUpdater::OPTION_ATTR.push(:ignore_channel_wide_mention)
UserUpdater::OPTION_ATTR.push(:chat_email_frequency) UserUpdater::OPTION_ATTR.push(:chat_email_frequency)
UserUpdater::OPTION_ATTR.push(:chat_header_indicator_preference) UserUpdater::OPTION_ATTR.push(:chat_header_indicator_preference)
UserUpdater::OPTION_ATTR.push(:chat_separate_sidebar_mode)
register_reviewable_type Chat::ReviewableMessage register_reviewable_type Chat::ReviewableMessage
@ -297,6 +298,12 @@ after_initialize do
object.chat_header_indicator_preference object.chat_header_indicator_preference
end end
add_to_serializer(:user_option, :chat_separate_sidebar_mode) { object.chat_separate_sidebar_mode }
add_to_serializer(:current_user_option, :chat_separate_sidebar_mode) do
object.chat_separate_sidebar_mode
end
RETENTION_SETTINGS_TO_USER_OPTION_FIELDS = { RETENTION_SETTINGS_TO_USER_OPTION_FIELDS = {
chat_channel_retention_days: :dismissed_channel_retention_reminder, chat_channel_retention_days: :dismissed_channel_retention_reminder,
chat_dm_retention_days: :dismissed_dm_retention_reminder, chat_dm_retention_days: :dismissed_dm_retention_reminder,

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
require "rails_helper"
RSpec.describe UserOption do
describe "#chat_separate_sidebar_mode" do
it "is present" do
expect(described_class.new.chat_separate_sidebar_mode).to eq("default")
end
end
end

View File

@ -13,6 +13,12 @@ RSpec.describe CurrentUserSerializer do
current_user.user_option.update(chat_enabled: true) current_user.user_option.update(chat_enabled: true)
end end
describe "#chat_separate_sidebar_mode" do
it "is present" do
expect(serializer.as_json[:user_option][:chat_separate_sidebar_mode]).to eq("default")
end
end
describe "#chat_drafts" do describe "#chat_drafts" do
context "when user can't chat" do context "when user can't chat" do
before { SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:staff] } before { SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:staff] }

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
RSpec.describe UserSerializer do
fab!(:current_user) { Fabricate(:user) }
let(:serializer) do
described_class.new(current_user, scope: Guardian.new(current_user), root: false)
end
describe "#chat_separate_sidebar_mode" do
it "is present" do
expect(serializer.as_json[:user_option][:chat_separate_sidebar_mode]).to eq("default")
end
end
end

View File

@ -19,6 +19,9 @@ RSpec.describe "Navigation", type: :system do
before do before do
chat_system_bootstrap(current_user, [category_channel, category_channel_2]) chat_system_bootstrap(current_user, [category_channel, category_channel_2])
current_user.user_option.update(
chat_separate_sidebar_mode: UserOption.chat_separate_sidebar_modes[:never],
)
sign_in(current_user) sign_in(current_user)
end end
@ -36,7 +39,6 @@ RSpec.describe "Navigation", type: :system do
context "when clicking chat icon on mobile and is viewing channel" do context "when clicking chat icon on mobile and is viewing channel" do
it "navigates to index", mobile: true do it "navigates to index", mobile: true do
visit("/chat")
chat_page.visit_channel(category_channel_2) chat_page.visit_channel(category_channel_2)
chat_page.open_from_header chat_page.open_from_header
@ -44,18 +46,6 @@ RSpec.describe "Navigation", type: :system do
end end
end end
context "when clicking chat icon on desktop and is viewing channel" do
it "stays on channel page" do
visit("/chat")
chat_page.visit_channel(category_channel_2)
chat_page.open_from_header
expect(page).to have_current_path(
chat.channel_path(category_channel_2.slug, category_channel_2.id),
)
end
end
context "when visiting /chat" do context "when visiting /chat" do
it "opens full page" do it "opens full page" do
chat_page.open chat_page.open

View File

@ -17,6 +17,12 @@ module PageObjects
) )
end end
def prefers_drawer
page.execute_script(
"window.localStorage.setItem('discourse_chat_preferred_mode', '\"DRAWER_CHAT\"');",
)
end
def open_from_header def open_from_header
find(".chat-header-icon").click find(".chat-header-icon").click
end end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
module PageObjects
module Components
module Chat
class Header < PageObjects::Components::Base
def has_open_chat_button?
has_css?(".d-header .chat-header-icon .d-icon-d-chat")
end
def has_open_forum_button?
has_css?(".d-header .chat-header-icon .d-icon-random")
end
def has_no_chat_button?
has_no_css?(".d-header .chat-header-icon")
end
end
end
end
end

View File

@ -0,0 +1,202 @@
# frozen_string_literal: true
RSpec.describe "Separate sidebar mode", type: :system do
let(:chat_page) { PageObjects::Pages::Chat.new }
let(:sidebar_page) { PageObjects::Pages::Sidebar.new }
let(:sidebar_component) { PageObjects::Components::NavigationMenu::Sidebar.new }
let(:chat_drawer_page) { PageObjects::Pages::ChatDrawer.new }
let(:header_component) { PageObjects::Components::Chat::Header.new }
fab!(:current_user) { Fabricate(:user) }
fab!(:channel_1) { Fabricate(:chat_channel) }
before do
SiteSetting.navigation_menu = "sidebar"
channel_1.add(current_user)
chat_system_bootstrap
sign_in(current_user)
end
describe "when separate sidebar mode is never" do
before do
current_user.user_option.update!(
chat_separate_sidebar_mode: UserOption.chat_separate_sidebar_modes[:never],
)
end
context "with drawer" do
before { chat_page.prefers_drawer }
it "has the expected behavior" do
visit("/")
expect(sidebar_component).to have_no_switch_button
expect(header_component).to have_open_chat_button
expect(sidebar_component).to have_section("Categories")
expect(sidebar_component).to have_section("chat-channels")
sidebar_page.open_channel(channel_1)
expect(sidebar_component).to have_no_switch_button
expect(header_component).to have_open_chat_button
expect(sidebar_component).to have_section("Categories")
expect(sidebar_component).to have_section("chat-channels")
chat_drawer_page.close
expect(sidebar_component).to have_no_switch_button
expect(header_component).to have_open_chat_button
expect(sidebar_component).to have_section("Categories")
expect(sidebar_component).to have_section("chat-channels")
end
end
context "with full page" do
before { chat_page.prefers_full_page }
it "has the expected behavior" do
visit("/")
expect(sidebar_component).to have_no_switch_button
expect(header_component).to have_open_chat_button
expect(sidebar_component).to have_section("Categories")
expect(sidebar_component).to have_section("chat-channels")
sidebar_page.open_channel(channel_1)
expect(sidebar_component).to have_no_switch_button
expect(header_component).to have_open_chat_button
expect(sidebar_component).to have_section("Categories")
expect(sidebar_component).to have_section("chat-channels")
find("#site-logo").click
expect(sidebar_component).to have_no_switch_button
expect(header_component).to have_open_chat_button
expect(sidebar_component).to have_section("Categories")
expect(sidebar_component).to have_section("chat-channels")
end
end
end
describe "when separate sidebar mode is always" do
before do
current_user.user_option.update(
chat_separate_sidebar_mode: UserOption.chat_separate_sidebar_modes[:always],
)
end
context "with drawer" do
before { chat_page.prefers_drawer }
it "has the expected behavior" do
visit("/")
expect(sidebar_component).to have_switch_button("chat")
expect(header_component).to have_open_chat_button
expect(sidebar_component).to have_no_section("chat-channels")
chat_page.open_from_header
expect(sidebar_component).to have_no_switch_button
expect(header_component).to have_open_chat_button
expect(sidebar_component).to have_no_section("chat-channels")
chat_drawer_page.close
expect(sidebar_component).to have_switch_button("chat")
expect(header_component).to have_open_chat_button
expect(sidebar_component).to have_no_section("chat-channels")
end
end
context "with full page" do
before { chat_page.prefers_full_page }
it "has the expected behavior" do
visit("/")
expect(sidebar_component).to have_switch_button("chat")
expect(header_component).to have_open_chat_button
expect(sidebar_component).to have_no_section("chat-channels")
expect(sidebar_component).to have_section("Categories")
chat_page.open_from_header
expect(sidebar_component).to have_switch_button("main")
expect(header_component).to have_open_forum_button
expect(sidebar_component).to have_section("chat-channels")
expect(sidebar_component).to have_no_section("Categories")
find("#site-logo").click
expect(sidebar_component).to have_switch_button("chat")
expect(header_component).to have_open_chat_button
expect(sidebar_component).to have_no_section("chat-channels")
expect(sidebar_component).to have_section("Categories")
end
end
end
describe "when separate sidebar mode is fullscreen" do
before do
current_user.user_option.update(
chat_separate_sidebar_mode: UserOption.chat_separate_sidebar_modes[:fullscreen],
)
end
context "with drawer" do
before { chat_page.prefers_drawer }
it "has the expected behavior" do
visit("/")
expect(sidebar_component).to have_switch_button
expect(header_component).to have_open_chat_button
expect(sidebar_component).to have_section("Categories")
expect(sidebar_component).to have_section("chat-channels")
sidebar_page.open_channel(channel_1)
expect(sidebar_component).to have_no_switch_button
expect(header_component).to have_open_chat_button
expect(sidebar_component).to have_section("Categories")
expect(sidebar_component).to have_section("chat-channels")
chat_drawer_page.close
expect(sidebar_component).to have_switch_button
expect(header_component).to have_open_chat_button
expect(sidebar_component).to have_section("Categories")
expect(sidebar_component).to have_section("chat-channels")
end
end
context "with full page" do
before { chat_page.prefers_full_page }
it "has the expected behavior" do
visit("/")
expect(sidebar_component).to have_switch_button("chat")
expect(header_component).to have_open_chat_button
expect(sidebar_component).to have_section("chat-channels")
expect(sidebar_component).to have_section("Categories")
sidebar_page.open_channel(channel_1)
expect(sidebar_component).to have_switch_button("main")
expect(header_component).to have_open_forum_button
expect(sidebar_component).to have_section("chat-channels")
expect(sidebar_component).to have_no_section("Categories")
find("#site-logo").click
expect(sidebar_component).to have_switch_button("chat")
expect(header_component).to have_open_chat_button
expect(sidebar_component).to have_section("chat-channels")
expect(sidebar_component).to have_section("Categories")
end
end
end
end

View File

@ -25,22 +25,32 @@ RSpec.describe "User chat preferences", type: :system do
it "can select chat sound" do it "can select chat sound" do
visit("/u/#{current_user.username}/preferences/chat") visit("/u/#{current_user.username}/preferences/chat")
find("#user_chat_sounds .select-kit-header[data-value]").click select_kit = PageObjects::Components::SelectKit.new("#user_chat_sounds")
find("[data-value='bell']").click select_kit.expand
select_kit.select_row_by_value("bell")
find(".save-changes").click find(".save-changes").click
expect(page).to have_css("#user_chat_sounds .select-kit-header[data-value='bell']") expect(select_kit).to have_selected_value("bell")
end end
it "can select header_indicator_preference" do it "can select header_indicator_preference" do
visit("/u/#{current_user.username}/preferences/chat") visit("/u/#{current_user.username}/preferences/chat")
find("#user_chat_header_indicator_preference .select-kit-header[data-value]").click select_kit = PageObjects::Components::SelectKit.new("#user_chat_header_indicator_preference")
find("[data-value='dm_and_mentions']").click select_kit.expand
select_kit.select_row_by_value("dm_and_mentions")
find(".save-changes").click find(".save-changes").click
expect(page).to have_css( expect(select_kit).to have_selected_value("dm_and_mentions")
"#user_chat_header_indicator_preference .select-kit-header[data-value='dm_and_mentions']", end
)
it "can select separate sidebar mode" do
visit("/u/#{current_user.username}/preferences/chat")
select_kit = PageObjects::Components::SelectKit.new("#user_chat_separate_sidebar_mode")
select_kit.expand
select_kit.select_row_by_value("fullscreen")
find(".save-changes").click
expect(select_kit).to have_selected_value("fullscreen")
end end
context "as an admin on another user's preferences" do context "as an admin on another user's preferences" do

View File

@ -0,0 +1,44 @@
import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import sinon from "sinon";
import I18n from "I18n";
module("Discourse Chat | Component | chat-header-icon", function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {});
test("full page - never separated sidebar mode", async function (assert) {
this.currentUser.user_option.chat_separate_sidebar_mode = "never";
sinon
.stub(this.owner.lookup("service:chat-state-manager"), "isFullPageActive")
.value(true);
await render(hbs`<Chat::Header::Icon />`);
assert
.dom(".icon.btn-flat")
.hasAttribute("title", I18n.t("chat.title_capitalized"))
.hasAttribute("href", "/chat");
assert.dom(".d-icon-d-chat").exists();
});
test("full page - always separated mode", async function (assert) {
this.currentUser.user_option.chat_separate_sidebar_mode = "always";
sinon
.stub(this.owner.lookup("service:chat-state-manager"), "isFullPageActive")
.value(true);
await render(hbs`<Chat::Header::Icon />`);
assert
.dom(".icon.btn-flat")
.hasAttribute("title", I18n.t("sidebar.panels.forum.label"))
.hasAttribute("href", "/latest");
assert.dom(".d-icon-random").exists();
});
});

View File

@ -34,6 +34,22 @@ module PageObjects
has_no_css?(".sidebar-sections [data-section-name='#{name.parameterize}']") has_no_css?(".sidebar-sections [data-section-name='#{name.parameterize}']")
end end
def has_switch_button?(key = nil)
if key
page.has_css?(".sidebar__panel-switch-button[data-key='#{key.parameterize}']")
else
page.has_css?(".sidebar__panel-switch-button")
end
end
def has_no_switch_button?(key = nil)
if key
page.has_no_css?(".sidebar__panel-switch-button[data-key='#{key.parameterize}']")
else
page.has_no_css?(".sidebar__panel-switch-button")
end
end
def has_categories_section? def has_categories_section?
has_section?("Categories") has_section?("Categories")
end end