FEATURE: allows to enable/disable threading in UI (#22307)

Enabling/Disabling threading has been possible through command line until now. This commit introduces two new UIs:

- When creating a channel, it will be available once the category has been selected
- On the settings page of a channel for admins
This commit is contained in:
Joffrey JAFFEUX 2023-06-29 07:19:12 +02:00 committed by GitHub
parent de2febcc0c
commit ea0b8ca38c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 383 additions and 156 deletions

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
CHANNEL_EDITABLE_PARAMS = %i[name description slug]
CATEGORY_CHANNEL_EDITABLE_PARAMS = %i[auto_join_users allow_channel_wide_mentions]
CATEGORY_CHANNEL_EDITABLE_PARAMS = %i[auto_join_users allow_channel_wide_mentions threading_enabled]
class Chat::Api::ChannelsController < Chat::ApiController
def index
@ -36,7 +36,14 @@ class Chat::Api::ChannelsController < Chat::ApiController
def create
channel_params =
params.require(:channel).permit(:chatable_id, :name, :slug, :description, :auto_join_users)
params.require(:channel).permit(
:chatable_id,
:name,
:slug,
:description,
:auto_join_users,
:threading_enabled,
)
# NOTE: We don't allow creating channels for anything but category chatable types
# at the moment. This may change in future, at which point we will need to pass in

View File

@ -10,6 +10,7 @@ module Chat
# description: "This is the best channel",
# slug: "super-channel",
# category_id: category.id,
# threading_enabled: true,
# )
#
class CreateCategoryChannel
@ -23,6 +24,7 @@ module Chat
# @option params_to_create [String] slug
# @option params_to_create [Boolean] auto_join_users
# @option params_to_create [Integer] category_id
# @option params_to_create [Boolean] threading_enabled
# @return [Service::Base::Context]
policy :can_create_channel
@ -42,8 +44,12 @@ module Chat
attribute :slug, :string
attribute :category_id, :integer
attribute :auto_join_users, :boolean, default: false
attribute :threading_enabled, :boolean, default: false
before_validation { self.auto_join_users = auto_join_users.presence || false }
before_validation do
self.auto_join_users = auto_join_users.presence || false
self.threading_enabled = threading_enabled.presence || false
end
validates :category_id, presence: true
validates :name, length: { maximum: SiteSetting.max_topic_title_length }
@ -70,6 +76,7 @@ module Chat
description: contract.description,
user_count: 1,
auto_join_users: contract.auto_join_users,
threading_enabled: contract.threading_enabled,
)
end

View File

@ -3,8 +3,8 @@
module Chat
# Service responsible for updating a chat channel's name, slug, and description.
#
# For a CategoryChannel, the settings for auto_join_users and allow_channel_wide_mentions
# are also editable.
# For a CategoryChannel, the settings for auto_join_users, allow_channel_wide_mentions
# and threading_enabled are also editable.
#
# @example
# Service::Chat::UpdateChannel.call(
@ -13,6 +13,7 @@ module Chat
# name: "SuperChannel",
# description: "This is the best channel",
# slug: "super-channel",
# threading_enaled: true,
# )
#
class UpdateChannel
@ -43,6 +44,7 @@ module Chat
attribute :name, :string
attribute :description, :string
attribute :slug, :string
attribute :threading_enabled, :boolean, default: false
attribute :auto_join_users, :boolean, default: false
attribute :allow_channel_wide_mentions, :boolean, default: true

View File

@ -1,36 +1,36 @@
<div class="chat-form__section">
<div class="chat-form__field">
<div class="chat-form__field -mute">
<label class="chat-form__label">
<span>{{i18n "chat.settings.mute"}}</span>
<ChatChannelSettingsSavedIndicator
@property={{this.channel.currentUserMembership.muted}}
@property={{@channel.currentUserMembership.muted}}
/>
</label>
<div class="chat-form__control">
<ComboBox
@content={{this.mutedOptions}}
@value={{this.channel.currentUserMembership.muted}}
@value={{@channel.currentUserMembership.muted}}
@valueProperty="value"
@class="channel-settings-view__muted-selector"
@class="channel-settings-view__selector"
@onChange={{fn this.saveNotificationSettings "muted" "muted"}}
/>
</div>
</div>
{{#unless this.channel.currentUserMembership.muted}}
<div class="chat-form__field">
{{#unless @channel.currentUserMembership.muted}}
<div class="chat-form__field -desktop-notification-level">
<label class="chat-form__label">
<span>{{i18n "chat.settings.desktop_notification_level"}}</span>
<ChatChannelSettingsSavedIndicator
@property={{this.channel.currentUserMembership.desktopNotificationLevel}}
@property={{@channel.currentUserMembership.desktopNotificationLevel}}
/>
</label>
<div class="chat-form__control">
<ComboBox
@content={{this.notificationLevels}}
@value={{this.channel.currentUserMembership.desktopNotificationLevel}}
@value={{@channel.currentUserMembership.desktopNotificationLevel}}
@valueProperty="value"
@class="channel-settings-view__desktop-notification-level-selector"
@class="channel-settings-view__selector"
@onChange={{fn
this.saveNotificationSettings
"desktopNotificationLevel"
@ -40,19 +40,19 @@
</div>
</div>
<div class="chat-form__field">
<div class="chat-form__field -mobile-notification-level">
<label class="chat-form__label">
<span>{{i18n "chat.settings.mobile_notification_level"}}</span>
<ChatChannelSettingsSavedIndicator
@property={{this.channel.currentUserMembership.mobileNotificationLevel}}
@property={{@channel.currentUserMembership.mobileNotificationLevel}}
/>
</label>
<div class="chat-form__control">
<ComboBox
@content={{this.notificationLevels}}
@value={{this.channel.currentUserMembership.mobileNotificationLevel}}
@value={{@channel.currentUserMembership.mobileNotificationLevel}}
@valueProperty="value"
@class="channel-settings-view__mobile-notification-level-selector"
@class="channel-settings-view__selector"
@onChange={{fn
this.saveNotificationSettings
"mobileNotificationLevel"
@ -64,7 +64,7 @@
{{/unless}}
<div class="chat-retention-info">
{{d-icon "info-circle"}}
<ChatRetentionReminderText @channel={{this.channel}} />
<ChatRetentionReminderText @channel={{@channel}} />
</div>
</div>
@ -72,28 +72,29 @@
<h3 class="chat-form__section-admin-title">
{{i18n "chat.settings.admin_title"}}
</h3>
{{#if this.autoJoinAvailable}}
<div class="chat-form__section">
<div class="chat-form__section -autojoin">
<div class="chat-form__field">
<label class="chat-form__label">
<span>{{i18n "chat.settings.auto_join_users_label"}}</span>
<ChatChannelSettingsSavedIndicator
@property={{this.channel.autoJoinUsers}}
@property={{@channel.autoJoinUsers}}
/>
</label>
<ComboBox
@content={{this.autoAddUsersOptions}}
@value={{this.channel.autoJoinUsers}}
@value={{@channel.autoJoinUsers}}
@valueProperty="value"
@class="channel-settings-view__auto-join-selector"
@class="channel-settings-view__selector"
@onChange={{action
(fn this.onToggleAutoJoinUsers this.channel.autoJoinUsers)
(fn this.onToggleAutoJoinUsers @channel.autoJoinUsers)
}}
/>
<p class="chat-form__description -autojoin">
<p class="chat-form__description">
{{i18n
"chat.settings.auto_join_users_info"
category=this.channel.chatable.name
category=@channel.chatable.name
}}
</p>
</div>
@ -101,36 +102,62 @@
{{/if}}
{{#if this.togglingChannelWideMentionsAvailable}}
<div class="chat-form__section">
<div class="chat-form__section -channel-wide-mentions">
<div class="chat-form__field">
<label class="chat-form__label">
<span>{{i18n "chat.settings.channel_wide_mentions_label"}}</span>
<ChatChannelSettingsSavedIndicator
@property={{this.channel.allowChannelWideMentions}}
@property={{@channel.allowChannelWideMentions}}
/>
</label>
<ComboBox
@content={{this.channelWideMentionsOptions}}
@value={{this.channel.allowChannelWideMentions}}
@value={{@channel.allowChannelWideMentions}}
@valueProperty="value"
@class="channel-settings-view__channel-wide-mentions-selector"
@class="channel-settings-view__selector"
@onChange={{this.onToggleChannelWideMentions}}
/>
<p class="chat-form__description -channel-wide-mentions">
<p class="chat-form__description">
{{i18n
"chat.settings.channel_wide_mentions_description"
channel=this.channel.title
channel=@channel.title
}}
</p>
</div>
</div>
{{/if}}
{{#if this.togglingThreadingAvailable}}
<div class="chat-form__section -threading">
<div class="chat-form__field">
<label class="chat-form__label">
<span>{{i18n "chat.settings.channel_threading_label"}}</span>
<span class="channel-settings-view__channel-threading-tooltip">
{{d-icon "info-circle"}}
<DTooltip>
{{i18n "chat.settings.channel_threading_description"}}
</DTooltip>
</span>
<ChatChannelSettingsSavedIndicator
@property={{@channel.threadingEnabled}}
/>
</label>
<ComboBox
@content={{this.threadingEnabledOptions}}
@value={{@channel.threadingEnabled}}
@valueProperty="value"
@class="channel-settings-view__selector"
@onChange={{this.onToggleThreadingEnabled}}
/>
</div>
</div>
{{/if}}
{{/if}}
{{#unless this.channel.isDirectMessageChannel}}
{{#unless @channel.isDirectMessageChannel}}
<div class="chat-form__section">
{{#if (chat-guardian "can-edit-chat-channel")}}
{{#if (chat-guardian "can-archive-channel" this.channel)}}
{{#if (chat-guardian "can-archive-channel" @channel)}}
<div class="chat-form__field">
<DButton
@action={{action "onArchiveChannel"}}
@ -141,7 +168,7 @@
</div>
{{/if}}
{{#if this.channel.isClosed}}
{{#if @channel.isClosed}}
<div class="chat-form__field">
<DButton
@action={{action "onToggleChannelState"}}

View File

@ -1,9 +1,8 @@
import Component from "@ember/component";
import { action, computed } from "@ember/object";
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import showModal from "discourse/lib/show-modal";
import I18n from "I18n";
import { reads } from "@ember/object/computed";
const NOTIFICATION_LEVELS = [
{ name: I18n.t("chat.notification_levels.never"), value: "never" },
@ -21,6 +20,11 @@ const AUTO_ADD_USERS_OPTIONS = [
{ name: I18n.t("no_value"), value: false },
];
const THREADING_ENABLED_OPTIONS = [
{ name: I18n.t("chat.settings.threading_enabled"), value: true },
{ name: I18n.t("chat.settings.threading_disabled"), value: false },
];
const CHANNEL_WIDE_MENTIONS_OPTIONS = [
{ name: I18n.t("yes_value"), value: true },
{
@ -33,13 +37,14 @@ export default class ChatChannelSettingsView extends Component {
@service chat;
@service chatApi;
@service chatGuardian;
@service currentUser;
@service siteSettings;
@service router;
@service dialog;
tagName = "";
channel = null;
notificationLevels = NOTIFICATION_LEVELS;
mutedOptions = MUTED_OPTIONS;
threadingEnabledOptions = THREADING_ENABLED_OPTIONS;
autoAddUsersOptions = AUTO_ADD_USERS_OPTIONS;
channelWideMentionsOptions = CHANNEL_WIDE_MENTIONS_OPTIONS;
isSavingNotificationSetting = false;
@ -47,17 +52,25 @@ export default class ChatChannelSettingsView extends Component {
savedMobileNotificationLevel = false;
savedMuted = false;
@reads("channel.isCategoryChannel") togglingChannelWideMentionsAvailable;
get togglingChannelWideMentionsAvailable() {
return this.args.channel.isCategoryChannel;
}
@computed("channel.isCategoryChannel")
get autoJoinAvailable() {
get togglingThreadingAvailable() {
return (
this.siteSettings.max_chat_auto_joined_users > 0 &&
this.channel.isCategoryChannel
this.siteSettings.enable_experimental_chat_threaded_discussions &&
this.args.channel.isCategoryChannel &&
this.currentUser?.admin
);
}
get autoJoinAvailable() {
return (
this.siteSettings.max_chat_auto_joined_users > 0 &&
this.args.channel.isCategoryChannel
);
}
@computed("autoJoinAvailable", "togglingChannelWideMentionsAvailable")
get adminSectionAvailable() {
return (
this.chatGuardian.canEditChatChannel() &&
@ -65,30 +78,29 @@ export default class ChatChannelSettingsView extends Component {
);
}
@computed(
"siteSettings.chat_allow_archiving_channels",
"channel.{isArchived,isReadOnly}"
)
get canArchiveChannel() {
return (
this.siteSettings.chat_allow_archiving_channels &&
!this.channel.isArchived &&
!this.channel.isReadOnly
!this.args.channel.isArchived &&
!this.args.channel.isReadOnly
);
}
@action
saveNotificationSettings(frontendKey, backendKey, newValue) {
if (this.channel.currentUserMembership[frontendKey] === newValue) {
if (this.args.channel.currentUserMembership[frontendKey] === newValue) {
return;
}
const settings = {};
settings[backendKey] = newValue;
return this.chatApi
.updateCurrentUserChannelNotificationsSettings(this.channel.id, settings)
.updateCurrentUserChannelNotificationsSettings(
this.args.channel.id,
settings
)
.then((result) => {
this.channel.currentUserMembership[frontendKey] =
this.args.channel.currentUserMembership[frontendKey] =
result.membership[backendKey];
});
}
@ -96,76 +108,89 @@ export default class ChatChannelSettingsView extends Component {
@action
onArchiveChannel() {
const controller = showModal("chat-channel-archive-modal");
controller.set("chatChannel", this.channel);
controller.set("chatChannel", this.args.channel);
}
@action
onDeleteChannel() {
const controller = showModal("chat-channel-delete-modal");
controller.set("chatChannel", this.channel);
controller.set("chatChannel", this.args.channel);
}
@action
onToggleChannelState() {
const controller = showModal("chat-channel-toggle");
controller.set("chatChannel", this.channel);
controller.set("chatChannel", this.args.channel);
}
@action
onToggleAutoJoinUsers() {
if (!this.channel.autoJoinUsers) {
if (!this.args.channel.autoJoinUsers) {
this.onEnableAutoJoinUsers();
} else {
this.onDisableAutoJoinUsers();
}
}
@action
onToggleThreadingEnabled(value) {
return this._updateChannelProperty(
this.args.channel,
"threading_enabled",
value
).then((result) => {
this.args.channel.threadingEnabled = result.channel.threading_enabled;
});
}
@action
onToggleChannelWideMentions() {
const newValue = !this.channel.allowChannelWideMentions;
if (this.channel.allowChannelWideMentions === newValue) {
const newValue = !this.args.channel.allowChannelWideMentions;
if (this.args.channel.allowChannelWideMentions === newValue) {
return;
}
return this._updateChannelProperty(
this.channel,
this.args.channel,
"allow_channel_wide_mentions",
newValue
).then((result) => {
this.channel.allowChannelWideMentions =
this.args.channel.allowChannelWideMentions =
result.channel.allow_channel_wide_mentions;
});
}
onDisableAutoJoinUsers() {
if (this.channel.autoJoinUsers === false) {
if (this.args.channel.autoJoinUsers === false) {
return;
}
return this._updateChannelProperty(
this.channel,
this.args.channel,
"auto_join_users",
false
).then((result) => {
this.channel.autoJoinUsers = result.channel.auto_join_users;
this.args.channel.autoJoinUsers = result.channel.auto_join_users;
});
}
onEnableAutoJoinUsers() {
if (this.channel.autoJoinUsers === true) {
if (this.args.channel.autoJoinUsers === true) {
return;
}
this.dialog.confirm({
message: I18n.t("chat.settings.auto_join_users_warning", {
category: this.channel.chatable.name,
category: this.args.channel.chatable.name,
}),
didConfirm: () =>
this._updateChannelProperty(this.channel, "auto_join_users", true).then(
(result) => {
this.channel.autoJoinUsers = result.channel.auto_join_users;
}
),
this._updateChannelProperty(
this.args.channel,
"auto_join_users",
true
).then((result) => {
this.args.channel.autoJoinUsers = result.channel.auto_join_users;
}),
});
}

View File

@ -26,6 +26,8 @@ export default class CreateChannelController extends Controller.extend(
@service chatChannelsManager;
@service chatApi;
@service router;
@service currentUser;
@service siteSettings;
category = null;
categoryId = null;
@ -37,10 +39,18 @@ export default class CreateChannelController extends Controller.extend(
autoJoinUsers = false;
autoJoinWarning = "";
loadingPermissionHint = false;
threadingEnabled = false;
@notEmpty("category") categorySelected;
@gt("siteSettings.max_chat_auto_joined_users", 0) autoJoinAvailable;
get threadingAvailable() {
return (
this.siteSettings.enable_experimental_chat_threaded_discussions &&
this.categorySelected
);
}
@computed("categorySelected", "name")
get createDisabled() {
return !this.categorySelected || isBlank(this.name);
@ -78,6 +88,7 @@ export default class CreateChannelController extends Controller.extend(
slug: this.slug || this.autoGeneratedSlug,
description: this.description,
auto_join_users: this.autoJoinUsers,
threading_enabled: this.threadingEnabled,
};
return this.chatApi

View File

@ -1,50 +1,50 @@
<DModalBody @title="chat.create_channel.title">
<div class="create-channel-control">
<label for="channel-name" class="create-channel-label">
<div class="create-channel__control -name">
<label for="channel-name" class="create-channel__label">
{{i18n "chat.create_channel.name"}}
</label>
<Input
name="channel-name"
class="create-channel-name-input"
class="create-channel__input"
@type="text"
@value={{this.name}}
{{on "input" (action "onNameChange" value="target.value")}}
/>
</div>
<div class="create-channel-control">
<label for="channel-slug" class="create-channel-label">
<div class="create-channel__control -slug">
<label for="channel-slug" class="create-channel__label">
{{i18n "chat.create_channel.slug"}}&nbsp;
<span>
{{d-icon "info-circle"}}
<DTooltip>{{i18n
"chat.channel_edit_name_slug_modal.slug_description"
}}</DTooltip>
<DTooltip>
{{i18n "chat.channel_edit_name_slug_modal.slug_description"}}
</DTooltip>
</span>
</label>
<Input
name="channel-slug"
class="create-channel-slug-input"
class="create-channel__input"
@type="text"
@value={{this.slug}}
placeholder={{this.autoGeneratedSlug}}
/>
</div>
<div class="create-channel-control">
<label for="channel-description" class="create-channel-label">
<div class="create-channel__control -description">
<label for="channel-description" class="create-channel__label">
{{i18n "chat.create_channel.description"}}
</label>
<Input
name="channel-description"
class="create-channel-description-input"
class="create-channel__input"
@type="textarea"
@value={{this.description}}
/>
</div>
<div class="create-channel-control">
<label class="create-channel-label">
<div class="create-channel__control">
<label class="create-channel__label">
{{i18n "chat.create_channel.choose_category.label"}}
</label>
<CategoryChooser
@ -56,7 +56,7 @@
{{#if this.categoryPermissionsHint}}
<div
class={{concat-class
"create-channel-hint"
"create-channel__hint"
(if this.loadingPermissionHint "loading-permissions")
}}
>
@ -66,14 +66,14 @@
</div>
{{#if this.autoJoinAvailable}}
<div class="create-channel-control">
<label class="create-channel-label">
<div class="create-channel__control -auto-join">
<label class="create-channel__label">
<Input @type="checkbox" @checked={{this.autoJoinUsers}} />
<div class="auto-join-channel">
<span class="auto-join-channel__label">
<span class="create-channel__label-title">
{{i18n "chat.settings.auto_join_users_label"}}
</span>
<p class="auto-join-channel__description">
<p class="create-channel__label-description">
{{#if this.categoryName}}
{{i18n
"chat.settings.auto_join_users_info"
@ -87,6 +87,22 @@
</label>
</div>
{{/if}}
{{#if this.threadingAvailable}}
<div class="create-channel__control -threading-toggle">
<label class="create-channel__label">
<Input @type="checkbox" @checked={{this.threadingEnabled}} />
<div class="threading-channel">
<span class="create-channel__label-title">
{{i18n "chat.create_channel.threading.label"}}
</span>
<p class="create-channel__label-description">
{{i18n "chat.settings.channel_threading_description"}}
</p>
</div>
</label>
</div>
{{/if}}
</DModalBody>
<div class="modal-footer">

View File

@ -37,14 +37,17 @@
font-size: var(--font-down-2);
}
.channel-settings-view__desktop-notification-level-selector,
.channel-settings-view__mobile-notification-level-selector,
.channel-settings-view__muted-selector,
.channel-settings-view__auto-join-selector,
.channel-settings-view__channel-wide-mentions-selector {
.channel-settings-view__selector {
width: 220px;
}
.channel-settings-view__channel-threading-tooltip {
padding-left: 0.25rem;
color: var(--tertiary);
cursor: pointer;
}
.channel-settings-view__muted-selector,
.chat-form__btn.delete-btn {
.d-icon {
color: var(--danger);

View File

@ -9,9 +9,7 @@
}
.select-kit.combo-box,
.create-channel-name-input,
.create-channel-slug-input,
.create-channel-description-input,
.create-channel__input,
#choose-topic-title {
width: 100%;
margin-bottom: 0;
@ -24,23 +22,21 @@
}
}
.create-channel-hint {
.create-channel__hint {
font-size: var(--font-down-1);
padding-top: 0.25rem;
color: var(--secondary-low);
}
.create-channel-control,
.create-channel__control,
.edit-channel-control {
margin-bottom: 1rem;
}
.auto-join-channel {
&__description {
.create-channel__label-description {
margin: 0;
padding-top: 0.25rem;
color: var(--secondary-low);
font-size: var(--font-down-1) !important;
}
}
}

View File

@ -354,6 +354,8 @@ en:
other: "%{count} members"
create_channel:
threading:
label: "Enable threading"
auto_join_users:
public_category_warning: "%{category} is a public category. Automatically add all recently active users to this channel?"
warning_1_group:
@ -431,6 +433,8 @@ en:
settings:
channel_wide_mentions_label: "Allow @all and @here mentions"
channel_wide_mentions_description: "Allow users to notify all members of #%{channel} with @all or only those who are active in the moment with @here"
channel_threading_label: "Threading"
channel_threading_description: "When threading is enabled, replies to a chat message will create a separate conversation, which will exist alongside the main channel."
auto_join_users_label: "Automatically add users"
auto_join_users_info: "Check hourly which users have been active in the last 3 months. Add them to this channel if they have access to the %{category} category."
auto_join_users_info_no_category: "Check hourly which users have been active in the last 3 months. Add them to this channel if they have access to the selected category."
@ -440,6 +444,8 @@ en:
followed: "Joined"
mobile_notification_level: "Mobile push notifications"
mute: "Mute channel"
threading_enabled: "Enabled"
threading_disabled: "Disabled"
muted_on: "On"
muted_off: "Off"
notifications: "Notifications"

View File

@ -629,6 +629,7 @@ RSpec.describe Chat::Api::ChannelsController do
chatable_id: category.id,
name: "channel name",
description: "My new channel",
threading_enabled: false,
},
}
end
@ -691,6 +692,25 @@ RSpec.describe Chat::Api::ChannelsController do
expect(new_channel.auto_join_users).to eq(true)
end
it "creates a channel sets threading_enabled to false by default" do
post "/chat/api/channels", params: params
expect(response.status).to eq(200)
new_channel = Chat::Channel.find(response.parsed_body.dig("channel", "id"))
expect(new_channel.threading_enabled).to eq(false)
end
it "creates a channel with threading_enabled set to true" do
params[:channel][:threading_enabled] = true
post "/chat/api/channels", params: params
expect(response.status).to eq(200)
new_channel = Chat::Channel.find(response.parsed_body.dig("channel", "id"))
expect(new_channel.threading_enabled).to eq(true)
end
describe "triggers the auto-join process" do
fab!(:chatters_group) { Fabricate(:group) }
fab!(:user) { Fabricate(:user, last_seen_at: 15.minute.ago) }
@ -884,6 +904,18 @@ RSpec.describe Chat::Api::ChannelsController do
expect(response.parsed_body["channel"]).to match_response_schema("category_chat_channel")
end
describe "when updating threading_enabled" do
before { SiteSetting.enable_experimental_chat_threaded_discussions = true }
it "sets the new value" do
expect {
put "/chat/api/channels/#{channel.id}", params: { channel: { threading_enabled: true } }
}.to change { channel.reload.threading_enabled }.from(false).to(true)
expect(response.parsed_body["channel"]["threading_enabled"]).to eq(true)
end
end
describe "when updating allow_channel_wide_mentions" do
it "sets the new value" do
put "/chat/api/channels/#{channel.id}",

View File

@ -93,6 +93,29 @@ RSpec.describe Chat::CreateCategoryChannel do
result
end
end
describe "threading_enabled" do
context "when true" do
it "sets threading_enabled to true" do
params[:threading_enabled] = true
expect(result.channel.threading_enabled).to eq(true)
end
end
context "when blank" do
it "sets threading_enabled to false" do
params[:threading_enabled] = nil
expect(result.channel.threading_enabled).to eq(false)
end
end
context "when false" do
it "sets threading_enabled to false" do
params[:threading_enabled] = false
expect(result.channel.threading_enabled).to eq(false)
end
end
end
end
end
end

View File

@ -63,7 +63,8 @@ RSpec.describe Chat::UpdateChannel do
)
end
context "when the name is blank" do
describe "name" do
context "when blank" do
before { params[:name] = "" }
it "nils out the name" do
@ -71,8 +72,10 @@ RSpec.describe Chat::UpdateChannel do
expect(channel.reload.name).to be_nil
end
end
end
context "when the description is blank" do
describe "description" do
context "when blank" do
before do
channel.update!(description: "something")
params[:description] = ""
@ -83,8 +86,10 @@ RSpec.describe Chat::UpdateChannel do
expect(channel.reload.description).to be_nil
end
end
end
context "when auto_join_users is set to 'true'" do
describe "#auto_join_users" do
context "when set to 'true'" do
before do
channel.update!(auto_join_users: false)
params[:auto_join_users] = true
@ -105,5 +110,28 @@ RSpec.describe Chat::UpdateChannel do
end
end
end
describe "threading_enabled" do
context "when true" do
it "changes the value to true" do
expect {
params[:threading_enabled] = true
result
}.to change { channel.reload.threading_enabled }.from(false).to(true)
end
end
context "when false" do
it "changes the value to true" do
channel.update!(threading_enabled: true)
expect {
params[:threading_enabled] = false
result
}.to change { channel.reload.threading_enabled }.from(true).to(false)
end
end
end
end
end
end

View File

@ -56,7 +56,7 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
context "as a member" do
before { channel_1.add(current_user) }
context "when visitng the settings of a recently joined channel" do
context "when visiting the settings of a recently joined channel" do
fab!(:channel_2) { Fabricate(:category_channel) }
it "is correctly populated" do
@ -82,8 +82,11 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
membership = channel_1.membership_for(current_user)
expect {
find(".channel-settings-view__muted-selector").click
find(".channel-settings-view__muted-selector [data-name='On']").click
select_kit =
PageObjects::Components::SelectKit.new(".-mute .channel-settings-view__selector")
select_kit.expand
select_kit.select_row_by_name("On")
expect(page).to have_content(I18n.t("js.chat.settings.saved"))
}.to change { membership.reload.muted }.from(false).to(true)
end
@ -93,10 +96,13 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
membership = channel_1.membership_for(current_user)
expect {
find(".channel-settings-view__desktop-notification-level-selector").click
find(
".channel-settings-view__desktop-notification-level-selector [data-name='Never']",
).click
select_kit =
PageObjects::Components::SelectKit.new(
".-desktop-notification-level .channel-settings-view__selector",
)
select_kit.expand
select_kit.select_row_by_name("Never")
expect(page).to have_content(I18n.t("js.chat.settings.saved"))
}.to change { membership.reload.desktop_notification_level }.from("mention").to("never")
end
@ -106,10 +112,13 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
membership = channel_1.membership_for(current_user)
expect {
find(".channel-settings-view__mobile-notification-level-selector").click
find(
".channel-settings-view__mobile-notification-level-selector [data-name='Never']",
).click
select_kit =
PageObjects::Components::SelectKit.new(
".-mobile-notification-level .channel-settings-view__selector",
)
select_kit.expand
select_kit.select_row_by_name("Never")
expect(page).to have_content(I18n.t("js.chat.settings.saved"))
}.to change { membership.reload.mobile_notification_level }.from("mention").to("never")
end
@ -133,9 +142,12 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
chat_page.visit_channel_settings(channel_1)
expect {
find(".channel-settings-view__auto-join-selector").click
find(".channel-settings-view__auto-join-selector [data-name='Yes']").click
select_kit =
PageObjects::Components::SelectKit.new(".-autojoin .channel-settings-view__selector")
select_kit.expand
select_kit.select_row_by_name("Yes")
find("#dialog-holder .btn-primary").click
expect(page).to have_content(I18n.t("js.chat.settings.saved"))
}.to change { channel_1.reload.auto_join_users }.from(false).to(true)
end
@ -144,8 +156,13 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
chat_page.visit_channel_settings(channel_1)
expect {
find(".channel-settings-view__channel-wide-mentions-selector").click
find(".channel-settings-view__channel-wide-mentions-selector [data-name='No']").click
select_kit =
PageObjects::Components::SelectKit.new(
".-channel-wide-mentions .channel-settings-view__selector",
)
select_kit.expand
select_kit.select_row_by_name("No")
expect(page).to have_content(I18n.t("js.chat.settings.saved"))
}.to change { channel_1.reload.allow_channel_wide_mentions }.from(true).to(false)
end
@ -160,6 +177,19 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
}.to change { channel_1.reload.status }.from("open").to("closed")
end
it "can enable threading" do
SiteSetting.enable_experimental_chat_threaded_discussions = true
chat_page.visit_channel_settings(channel_1)
expect {
select_kit =
PageObjects::Components::SelectKit.new(".-threading .channel-settings-view__selector")
select_kit.expand
select_kit.select_row_by_name("Enabled")
expect(page).to have_content(I18n.t("js.chat.settings.saved"))
}.to change { channel_1.reload.threading_enabled }.from(false).to(true)
end
it "can delete channel" do
chat_page.visit_channel_settings(channel_1)

View File

@ -32,6 +32,16 @@ RSpec.describe "Create channel", type: :system do
expect(channel_modal).to have_create_hint(Group[:everyone].name)
end
it "shows threading toggle" do
SiteSetting.enable_experimental_chat_threaded_discussions = true
chat_page.visit_browse
chat_page.new_channel_button.click
channel_modal.select_category(category_1)
expect(channel_modal).to have_threading_toggle
end
it "does not override channel name if that was already specified" do
chat_page.visit_browse
chat_page.new_channel_button.click
@ -138,7 +148,7 @@ RSpec.describe "Create channel", type: :system do
context "for a public category" do
before do
channel_modal.select_category(category_1)
find(".auto-join-channel__label").click
find(".-auto-join .create-channel__label").click
channel_modal.click_primary_button
end
@ -175,7 +185,7 @@ RSpec.describe "Create channel", type: :system do
before do
group_1.add(user_1)
channel_modal.select_category(private_category)
find(".auto-join-channel__label").click
find(".-auto-join .create-channel__label").click
channel_modal.click_primary_button
end

View File

@ -9,17 +9,21 @@ module PageObjects
end
def create_channel_hint
find(".create-channel-hint")
find(".create-channel__hint")
end
def slug_input
find(".create-channel-slug-input")
find(".-slug .create-channel__input")
end
def has_create_hint?(content)
create_channel_hint.has_content?(content)
end
def has_threading_toggle?
has_selector?(".create-channel__control.-threading-toggle")
end
def fill_name(name)
fill_in("channel-name", with: name)
end