mirror of
https://github.com/discourse/discourse.git
synced 2025-03-20 14:42:00 +08:00
UI: redesigned settings/members (#23804)
This PR is a first step towards private groups. It redesigns settings/members area of a channel and also drops the "about" page which is now mixed into settings. This commit is also: - introducing chat-form, a small DSL to create forms, ideally I would want something in core for this - introducing a DToggleSwitch page object component to simplify testing toggles - migrating various components to gjs
This commit is contained in:
parent
93c96cf6fa
commit
42801c950f
@ -1,12 +1,14 @@
|
|||||||
import { categoryLinkHTML } from "discourse/helpers/category-link";
|
import { categoryLinkHTML } from "discourse/helpers/category-link";
|
||||||
import { registerUnbound } from "discourse-common/lib/helpers";
|
|
||||||
import { isPresent } from "@ember/utils";
|
import { isPresent } from "@ember/utils";
|
||||||
|
import { registerRawHelper } from "discourse-common/lib/helpers";
|
||||||
|
|
||||||
registerUnbound("category-badge", function (cat, options) {
|
registerRawHelper("category-badge", categoryBadge);
|
||||||
|
|
||||||
|
export default function categoryBadge(cat, options = {}) {
|
||||||
return categoryLinkHTML(cat, {
|
return categoryLinkHTML(cat, {
|
||||||
hideParent: options.hideParent,
|
hideParent: options.hideParent,
|
||||||
allowUncategorized: options.allowUncategorized,
|
allowUncategorized: options.allowUncategorized,
|
||||||
categoryStyle: options.categoryStyle,
|
categoryStyle: options.categoryStyle,
|
||||||
link: isPresent(options.link) ? options.link : false,
|
link: isPresent(options.link) ? options.link : false,
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { emojiUnescape } from "discourse/lib/text";
|
import { emojiUnescape } from "discourse/lib/text";
|
||||||
import { htmlSafe, isHTMLSafe } from "@ember/template";
|
import { htmlSafe, isHTMLSafe } from "@ember/template";
|
||||||
import { registerUnbound } from "discourse-common/lib/helpers";
|
|
||||||
import { escapeExpression } from "discourse/lib/utilities";
|
import { escapeExpression } from "discourse/lib/utilities";
|
||||||
|
import { registerRawHelper } from "discourse-common/lib/helpers";
|
||||||
|
|
||||||
registerUnbound("replace-emoji", (text, options) => {
|
registerRawHelper("replace-emoji", replaceEmoji);
|
||||||
|
|
||||||
|
export default function replaceEmoji(text, options) {
|
||||||
text = isHTMLSafe(text) ? text.toString() : escapeExpression(text);
|
text = isHTMLSafe(text) ? text.toString() : escapeExpression(text);
|
||||||
return htmlSafe(emojiUnescape(text, options));
|
return htmlSafe(emojiUnescape(text, options));
|
||||||
});
|
}
|
||||||
|
@ -70,7 +70,7 @@ import DDefaultToast from "float-kit/components/d-default-toast";
|
|||||||
export const TOAST = {
|
export const TOAST = {
|
||||||
options: {
|
options: {
|
||||||
autoClose: true,
|
autoClose: true,
|
||||||
duration: 10000,
|
duration: 3000,
|
||||||
component: DDefaultToast,
|
component: DDefaultToast,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -12,7 +12,6 @@ export default function () {
|
|||||||
"channel.info",
|
"channel.info",
|
||||||
{ path: "/c/:channelTitle/:channelId/info" },
|
{ path: "/c/:channelTitle/:channelId/info" },
|
||||||
function () {
|
function () {
|
||||||
this.route("about", { path: "/about" });
|
|
||||||
this.route("members", { path: "/members" });
|
this.route("members", { path: "/members" });
|
||||||
this.route("settings", { path: "/settings" });
|
this.route("settings", { path: "/settings" });
|
||||||
}
|
}
|
||||||
|
@ -1,93 +0,0 @@
|
|||||||
{{#if this.channel.isCategoryChannel}}
|
|
||||||
<div class="chat-form__section">
|
|
||||||
<div class="chat-form__field">
|
|
||||||
<label class="chat-form__label">
|
|
||||||
{{i18n "chat.about_view.associated_category"}}
|
|
||||||
</label>
|
|
||||||
<div class="chat-form__control">
|
|
||||||
{{category-badge
|
|
||||||
this.channel.chatable
|
|
||||||
link=true
|
|
||||||
allowUncategorized=true
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="chat-form__section">
|
|
||||||
<div class="chat-form__field">
|
|
||||||
<label class="chat-form__label">
|
|
||||||
<span>{{i18n "chat.about_view.name"}}</span>
|
|
||||||
{{#if (chat-guardian "can-edit-chat-channel")}}
|
|
||||||
<div class="chat-form__label-actions">
|
|
||||||
<DButton
|
|
||||||
@label="chat.channel_settings.edit"
|
|
||||||
@action={{if this.onEditChatChannelName this.onEditChatChannelName}}
|
|
||||||
class="edit-name-slug-btn btn-flat"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</label>
|
|
||||||
<div class="chat-form__control">
|
|
||||||
<div class="channel-info-about-view__name">
|
|
||||||
{{replace-emoji this.channel.title}}
|
|
||||||
</div>
|
|
||||||
<div class="channel-info-about-view__slug">
|
|
||||||
{{this.channel.slug}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if
|
|
||||||
(or (chat-guardian "can-edit-chat-channel") this.channel.description.length)
|
|
||||||
}}
|
|
||||||
<div class="chat-form__section">
|
|
||||||
<div class="chat-form__field">
|
|
||||||
<label class="chat-form__label">
|
|
||||||
<span>{{i18n "chat.about_view.description"}}</span>
|
|
||||||
{{#if (chat-guardian "can-edit-chat-channel")}}
|
|
||||||
<div class="chat-form__label-actions">
|
|
||||||
<DButton
|
|
||||||
@label={{if
|
|
||||||
this.channel.description.length
|
|
||||||
"chat.channel_settings.edit"
|
|
||||||
"chat.channel_settings.add"
|
|
||||||
}}
|
|
||||||
@action={{if
|
|
||||||
this.onEditChatChannelDescription
|
|
||||||
this.onEditChatChannelDescription
|
|
||||||
}}
|
|
||||||
class="edit-description-btn btn-flat"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div class="chat-form__control">
|
|
||||||
<div class="channel-info-about-view__description">
|
|
||||||
{{#if this.channel.description.length}}
|
|
||||||
{{this.channel.description}}
|
|
||||||
{{else}}
|
|
||||||
<div class="channel-info-about-view__description__helper-text">
|
|
||||||
{{i18n "chat.channel_edit_description_modal.description"}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="chat-form__section">
|
|
||||||
<ToggleChannelMembershipButton
|
|
||||||
@channel={{this.channel}}
|
|
||||||
@options={{hash
|
|
||||||
joinClass="btn-primary"
|
|
||||||
leaveClass="btn-flat"
|
|
||||||
joinIcon="sign-in-alt"
|
|
||||||
leaveIcon="sign-out-alt"
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
@ -1,11 +0,0 @@
|
|||||||
import Component from "@ember/component";
|
|
||||||
import { inject as service } from "@ember/service";
|
|
||||||
|
|
||||||
export default class ChatChannelAboutView extends Component {
|
|
||||||
@service chat;
|
|
||||||
tagName = "";
|
|
||||||
channel = null;
|
|
||||||
onEditChatChannelName = null;
|
|
||||||
onEditChatChannelDescription = null;
|
|
||||||
isLoading = false;
|
|
||||||
}
|
|
@ -0,0 +1,85 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
|
import { LinkTo } from "@ember/routing";
|
||||||
|
import ChatChannelTitle from "discourse/plugins/chat/discourse/components/chat-channel-title";
|
||||||
|
import ChatChannelStatus from "discourse/plugins/chat/discourse/components/chat-channel-status";
|
||||||
|
import I18n from "I18n";
|
||||||
|
|
||||||
|
export default class ChatChannelMessageEmojiPicker extends Component {
|
||||||
|
<template>
|
||||||
|
<div class="chat-full-page-header">
|
||||||
|
<div class="chat-channel-header-details">
|
||||||
|
<div class="chat-full-page-header__left-actions">
|
||||||
|
{{#if this.chatChannelInfoRouteOriginManager.isBrowse}}
|
||||||
|
<LinkTo
|
||||||
|
@route="chat.browse"
|
||||||
|
class="chat-full-page-header__back-btn no-text btn-flat btn"
|
||||||
|
title={{this.backToAllChannelsLabel}}
|
||||||
|
>
|
||||||
|
{{icon "chevron-left"}}
|
||||||
|
</LinkTo>
|
||||||
|
{{else}}
|
||||||
|
<LinkTo
|
||||||
|
@route="chat.channel"
|
||||||
|
@models={{@channel.routeModels}}
|
||||||
|
class="chat-full-page-header__back-btn no-text btn-flat btn"
|
||||||
|
title={{this.backToChannelLabel}}
|
||||||
|
>
|
||||||
|
{{icon "chevron-left"}}
|
||||||
|
</LinkTo>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ChatChannelTitle @channel={{@channel}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ChatChannelStatus @channel={{@channel}} />
|
||||||
|
|
||||||
|
<div class="chat-channel-info">
|
||||||
|
{{#if this.showTabs}}
|
||||||
|
<nav class="chat-channel-info__nav">
|
||||||
|
<ul class="nav nav-pills">
|
||||||
|
<li>
|
||||||
|
<LinkTo
|
||||||
|
@route="chat.channel.info.settings"
|
||||||
|
@model={{@channel}}
|
||||||
|
@replace={{true}}
|
||||||
|
>
|
||||||
|
{{this.settingsLabel}}
|
||||||
|
</LinkTo>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<LinkTo
|
||||||
|
@route="chat.channel.info.members"
|
||||||
|
@model={{@channel}}
|
||||||
|
@replace={{true}}
|
||||||
|
>
|
||||||
|
{{this.membersLabel}}
|
||||||
|
</LinkTo>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{outlet}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
@service chatChannelInfoRouteOriginManager;
|
||||||
|
@service site;
|
||||||
|
|
||||||
|
membersLabel = I18n.t("chat.channel_info.tabs.members");
|
||||||
|
settingsLabel = I18n.t("chat.channel_info.tabs.settings");
|
||||||
|
backToChannelLabel = I18n.t("chat.channel_info.back_to_all_channel");
|
||||||
|
backToAllChannelsLabel = I18n.t("chat.channel_info.back_to_channel");
|
||||||
|
|
||||||
|
get showTabs() {
|
||||||
|
return (
|
||||||
|
this.site.desktopView &&
|
||||||
|
this.args.channel.membershipsCount > 1 &&
|
||||||
|
this.args.channel.isOpen
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,42 +0,0 @@
|
|||||||
{{#if (gt this.channel.membershipsCount 0)}}
|
|
||||||
<LoadMore @selector=".channel-members-view__list-item" @action={{this.load}}>
|
|
||||||
<div class="channel-members-view-wrapper">
|
|
||||||
<div
|
|
||||||
class={{concat
|
|
||||||
"channel-members-view__search-input-container"
|
|
||||||
(if this.isSearchFocused " is-focused")
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
class={{this.inputSelector}}
|
|
||||||
placeholder={{i18n "chat.members_view.filter_placeholder"}}
|
|
||||||
{{on "input" (action "onFilterMembers" value="target.value")}}
|
|
||||||
{{on "focusin" (action (mut this.isSearchFocused) true)}}
|
|
||||||
{{on "focusout" (action (mut this.isSearchFocused) false)}}
|
|
||||||
/>
|
|
||||||
{{d-icon "search"}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="channel-members-view__list-container">
|
|
||||||
<div role="list" class="channel-members-view__list">
|
|
||||||
{{#each this.members as |membership|}}
|
|
||||||
<div class="channel-members-view__list-item">
|
|
||||||
<ChatUserInfo @user={{membership.user}} />
|
|
||||||
</div>
|
|
||||||
{{else}}
|
|
||||||
{{#if this.members.fetchedOnce}}
|
|
||||||
<div class="chat-thread-list__no-threads">
|
|
||||||
{{i18n "chat.channel.no_memberships_found"}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ConditionalLoadingSpinner @condition={{this.members.loading}} />
|
|
||||||
</LoadMore>
|
|
||||||
{{else}}
|
|
||||||
<div class="channel-members-view-wrapper">
|
|
||||||
{{i18n "chat.channel.no_memberships"}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
@ -1,66 +0,0 @@
|
|||||||
import { INPUT_DELAY } from "discourse-common/config/environment";
|
|
||||||
import Component from "@ember/component";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { schedule } from "@ember/runloop";
|
|
||||||
import discourseDebounce from "discourse-common/lib/debounce";
|
|
||||||
import { inject as service } from "@ember/service";
|
|
||||||
|
|
||||||
export default class ChatChannelMembersView extends Component {
|
|
||||||
@service chatApi;
|
|
||||||
|
|
||||||
tagName = "";
|
|
||||||
channel = null;
|
|
||||||
isSearchFocused = false;
|
|
||||||
onlineUsers = null;
|
|
||||||
filter = null;
|
|
||||||
inputSelector = "channel-members-view__search-input";
|
|
||||||
members = null;
|
|
||||||
|
|
||||||
didInsertElement() {
|
|
||||||
this._super(...arguments);
|
|
||||||
|
|
||||||
if (!this.channel) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._focusSearch();
|
|
||||||
this.set("members", this.chatApi.listChannelMemberships(this.channel.id));
|
|
||||||
this.members.load();
|
|
||||||
|
|
||||||
this.appEvents.on("chat:refresh-channel-members", this, "onFilterMembers");
|
|
||||||
}
|
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
this._super(...arguments);
|
|
||||||
|
|
||||||
this.appEvents.off("chat:refresh-channel-members", this, "onFilterMembers");
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
onFilterMembers(username) {
|
|
||||||
this.set("filter", username);
|
|
||||||
this.set("members", this.chatApi.listChannelMemberships(this.channel.id));
|
|
||||||
|
|
||||||
discourseDebounce(
|
|
||||||
this,
|
|
||||||
this.members.load,
|
|
||||||
{ username: this.filter },
|
|
||||||
INPUT_DELAY
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
load() {
|
|
||||||
discourseDebounce(this, this.members.load, INPUT_DELAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
_focusSearch() {
|
|
||||||
if (this.capabilities.isIpadOS || this.site.mobileView) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
schedule("afterRender", () => {
|
|
||||||
document.getElementsByClassName(this.inputSelector)[0]?.focus();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,125 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
import ChatUserInfo from "discourse/plugins/chat/discourse/components/chat-user-info";
|
||||||
|
import gt from "truth-helpers/helpers/gt";
|
||||||
|
import { cached, tracked } from "@glimmer/tracking";
|
||||||
|
import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||||
|
import discourseDebounce from "discourse-common/lib/debounce";
|
||||||
|
import { modifier } from "ember-modifier";
|
||||||
|
import isElementInViewport from "discourse/lib/is-element-in-viewport";
|
||||||
|
import DcFilterInput from "discourse/plugins/chat/discourse/components/dc-filter-input";
|
||||||
|
import I18n from "I18n";
|
||||||
|
import { hash } from "@ember/helper";
|
||||||
|
import { schedule } from "@ember/runloop";
|
||||||
|
|
||||||
|
export default class ChatChannelMembers extends Component {
|
||||||
|
<template>
|
||||||
|
{{! template-lint-disable modifier-name-case }}
|
||||||
|
<div class="chat-channel-members">
|
||||||
|
<DcFilterInput
|
||||||
|
@class="chat-channel-members__filter"
|
||||||
|
@filterAction={{this.mutFilter}}
|
||||||
|
@icons={{hash right="search"}}
|
||||||
|
placeholder={{this.filterPlaceholder}}
|
||||||
|
{{this.focusInput}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{{#if (gt @channel.membershipsCount 0)}}
|
||||||
|
<ul class="chat-channel-members__list" {{this.fill}}>
|
||||||
|
{{#each this.members as |membership|}}
|
||||||
|
<li class="chat-channel-members__list-item">
|
||||||
|
<ChatUserInfo @user={{membership.user}} @avatarSize="tiny" />
|
||||||
|
</li>
|
||||||
|
{{else}}
|
||||||
|
{{#if this.noResults}}
|
||||||
|
<li
|
||||||
|
class="chat-channel-members__list-item -no-results alert alert-info"
|
||||||
|
>
|
||||||
|
{{this.noMembershipsFoundLabel}}
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div {{this.loadMore}}>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<p class="alert alert-info">
|
||||||
|
{{this.noMembershipsLabel}}
|
||||||
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
@service chatApi;
|
||||||
|
@service modal;
|
||||||
|
@service loadingSlider;
|
||||||
|
|
||||||
|
@tracked filter = "";
|
||||||
|
|
||||||
|
filterPlaceholder = I18n.t("chat.members_view.filter_placeholder");
|
||||||
|
noMembershipsFoundLabel = I18n.t("chat.channel.no_memberships_found");
|
||||||
|
noMembershipsLabel = I18n.t("chat.channel.no_memberships");
|
||||||
|
|
||||||
|
focusInput = modifier((element) => {
|
||||||
|
schedule("afterRender", () => {
|
||||||
|
element.focus();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
fill = modifier((element) => {
|
||||||
|
this.resizeObserver = new ResizeObserver(() => {
|
||||||
|
if (isElementInViewport(element)) {
|
||||||
|
this.load();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.resizeObserver.observe(element);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
this.resizeObserver.disconnect();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
loadMore = modifier((element) => {
|
||||||
|
this.intersectionObserver = new IntersectionObserver(this.load);
|
||||||
|
this.intersectionObserver.observe(element);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
this.intersectionObserver.disconnect();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
get noResults() {
|
||||||
|
return this.members.fetchedOnce && !this.members.loading;
|
||||||
|
}
|
||||||
|
|
||||||
|
@cached
|
||||||
|
get members() {
|
||||||
|
const params = {};
|
||||||
|
if (this.filter?.length) {
|
||||||
|
params.username = this.filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.chatApi.listChannelMemberships(this.args.channel.id, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
load() {
|
||||||
|
discourseDebounce(this, this.debouncedLoad, INPUT_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
mutFilter(event) {
|
||||||
|
this.filter = event.target.value;
|
||||||
|
this.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
async debouncedLoad() {
|
||||||
|
this.loadingSlider.transitionStarted();
|
||||||
|
await this.members.load({ limit: 20 });
|
||||||
|
this.loadingSlider.transitionEnded();
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +0,0 @@
|
|||||||
<span
|
|
||||||
{{did-update this.activate @property}}
|
|
||||||
{{will-destroy this.teardown}}
|
|
||||||
class={{concat-class
|
|
||||||
"chat-channel-settings-saved-indicator"
|
|
||||||
(if this.isActive "is-active")
|
|
||||||
}}
|
|
||||||
role="status"
|
|
||||||
>
|
|
||||||
{{#if this.isActive}}
|
|
||||||
{{d-icon "check"}}
|
|
||||||
<span>{{i18n "saved"}}</span>
|
|
||||||
{{/if}}
|
|
||||||
</span>
|
|
@ -1,28 +0,0 @@
|
|||||||
import discourseLater from "discourse-common/lib/later";
|
|
||||||
import Component from "@glimmer/component";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { tracked } from "@glimmer/tracking";
|
|
||||||
import { cancel } from "@ember/runloop";
|
|
||||||
|
|
||||||
const ACTIVE_DURATION = 2000;
|
|
||||||
|
|
||||||
export default class ChatChannelSettingsSavedIndicator extends Component {
|
|
||||||
@tracked isActive = false;
|
|
||||||
property = null;
|
|
||||||
|
|
||||||
@action
|
|
||||||
activate() {
|
|
||||||
cancel(this._deactivateHandler);
|
|
||||||
|
|
||||||
this.isActive = true;
|
|
||||||
|
|
||||||
this._deactivateHandler = discourseLater(() => {
|
|
||||||
this.isActive = false;
|
|
||||||
}, ACTIVE_DURATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
teardown() {
|
|
||||||
cancel(this._deactivateHandler);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,199 +0,0 @@
|
|||||||
<div class="chat-form__section">
|
|
||||||
<div class="chat-form__field -mute">
|
|
||||||
<label class="chat-form__label">
|
|
||||||
<span>{{i18n "chat.settings.mute"}}</span>
|
|
||||||
<ChatChannelSettingsSavedIndicator
|
|
||||||
@property={{@channel.currentUserMembership.muted}}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<div class="chat-form__control">
|
|
||||||
<ComboBox
|
|
||||||
@content={{this.mutedOptions}}
|
|
||||||
@value={{@channel.currentUserMembership.muted}}
|
|
||||||
@valueProperty="value"
|
|
||||||
@class="channel-settings-view__selector"
|
|
||||||
@onChange={{fn this.saveNotificationSettings "muted" "muted"}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#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={{@channel.currentUserMembership.desktopNotificationLevel}}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<div class="chat-form__control">
|
|
||||||
<ComboBox
|
|
||||||
@content={{this.notificationLevels}}
|
|
||||||
@value={{@channel.currentUserMembership.desktopNotificationLevel}}
|
|
||||||
@valueProperty="value"
|
|
||||||
@class="channel-settings-view__selector"
|
|
||||||
@onChange={{fn
|
|
||||||
this.saveNotificationSettings
|
|
||||||
"desktopNotificationLevel"
|
|
||||||
"desktop_notification_level"
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="chat-form__field -mobile-notification-level">
|
|
||||||
<label class="chat-form__label">
|
|
||||||
<span>{{i18n "chat.settings.mobile_notification_level"}}</span>
|
|
||||||
<ChatChannelSettingsSavedIndicator
|
|
||||||
@property={{@channel.currentUserMembership.mobileNotificationLevel}}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<div class="chat-form__control">
|
|
||||||
<ComboBox
|
|
||||||
@content={{this.notificationLevels}}
|
|
||||||
@value={{@channel.currentUserMembership.mobileNotificationLevel}}
|
|
||||||
@valueProperty="value"
|
|
||||||
@class="channel-settings-view__selector"
|
|
||||||
@onChange={{fn
|
|
||||||
this.saveNotificationSettings
|
|
||||||
"mobileNotificationLevel"
|
|
||||||
"mobile_notification_level"
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/unless}}
|
|
||||||
<div class="chat-retention-info">
|
|
||||||
{{d-icon "info-circle"}}
|
|
||||||
<ChatRetentionReminderText @channel={{@channel}} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if this.adminSectionAvailable}}
|
|
||||||
<h3 class="chat-form__section-admin-title">
|
|
||||||
{{i18n "chat.settings.admin_title"}}
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
{{#if this.autoJoinAvailable}}
|
|
||||||
<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={{@channel.autoJoinUsers}}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<ComboBox
|
|
||||||
@content={{this.autoAddUsersOptions}}
|
|
||||||
@value={{@channel.autoJoinUsers}}
|
|
||||||
@valueProperty="value"
|
|
||||||
@class="channel-settings-view__selector"
|
|
||||||
@onChange={{action
|
|
||||||
(fn this.onToggleAutoJoinUsers @channel.autoJoinUsers)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<p class="chat-form__description">
|
|
||||||
{{i18n
|
|
||||||
"chat.settings.auto_join_users_info"
|
|
||||||
category=@channel.chatable.name
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.togglingChannelWideMentionsAvailable}}
|
|
||||||
<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={{@channel.allowChannelWideMentions}}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<ComboBox
|
|
||||||
@content={{this.channelWideMentionsOptions}}
|
|
||||||
@value={{@channel.allowChannelWideMentions}}
|
|
||||||
@valueProperty="value"
|
|
||||||
@class="channel-settings-view__selector"
|
|
||||||
@onChange={{this.onToggleChannelWideMentions}}
|
|
||||||
/>
|
|
||||||
<p class="chat-form__description">
|
|
||||||
{{i18n
|
|
||||||
"chat.settings.channel_wide_mentions_description"
|
|
||||||
channel=@channel.title
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<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}}
|
|
||||||
|
|
||||||
{{#unless @channel.isDirectMessageChannel}}
|
|
||||||
<div class="chat-form__section">
|
|
||||||
{{#if (chat-guardian "can-edit-chat-channel")}}
|
|
||||||
{{#if (chat-guardian "can-archive-channel" @channel)}}
|
|
||||||
<div class="chat-form__field">
|
|
||||||
<DButton
|
|
||||||
@action={{this.onArchiveChannel}}
|
|
||||||
@label="chat.channel_settings.archive_channel"
|
|
||||||
@icon="archive"
|
|
||||||
class="archive-btn chat-form__btn btn-flat"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if @channel.isClosed}}
|
|
||||||
<div class="chat-form__field">
|
|
||||||
<DButton
|
|
||||||
@action={{this.onToggleChannelState}}
|
|
||||||
@label="chat.channel_settings.open_channel"
|
|
||||||
@icon="unlock"
|
|
||||||
class="open-btn chat-form__btn btn-flat"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{{else}}
|
|
||||||
<div class="chat-form__field">
|
|
||||||
<DButton
|
|
||||||
@action={{this.onToggleChannelState}}
|
|
||||||
@label="chat.channel_settings.close_channel"
|
|
||||||
@icon="lock"
|
|
||||||
class="close-btn chat-form__btn btn-flat"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="chat-form__field">
|
|
||||||
<DButton
|
|
||||||
@action={{this.onDeleteChannel}}
|
|
||||||
@label="chat.channel_settings.delete_channel"
|
|
||||||
@icon="trash-alt"
|
|
||||||
class="delete-btn chat-form__btn btn-flat"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/unless}}
|
|
@ -1,203 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { inject as service } from "@ember/service";
|
|
||||||
import I18n from "I18n";
|
|
||||||
import ChatModalArchiveChannel from "discourse/plugins/chat/discourse/components/chat/modal/archive-channel";
|
|
||||||
import ChatModalDeleteChannel from "discourse/plugins/chat/discourse/components/chat/modal/delete-channel";
|
|
||||||
import ChatModalToggleChannelStatus from "discourse/plugins/chat/discourse/components/chat/modal/toggle-channel-status";
|
|
||||||
|
|
||||||
const NOTIFICATION_LEVELS = [
|
|
||||||
{ name: I18n.t("chat.notification_levels.never"), value: "never" },
|
|
||||||
{ name: I18n.t("chat.notification_levels.mention"), value: "mention" },
|
|
||||||
{ name: I18n.t("chat.notification_levels.always"), value: "always" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const MUTED_OPTIONS = [
|
|
||||||
{ name: I18n.t("chat.settings.muted_on"), value: true },
|
|
||||||
{ name: I18n.t("chat.settings.muted_off"), value: false },
|
|
||||||
];
|
|
||||||
|
|
||||||
const AUTO_ADD_USERS_OPTIONS = [
|
|
||||||
{ name: I18n.t("yes_value"), value: true },
|
|
||||||
{ 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 },
|
|
||||||
{
|
|
||||||
name: I18n.t("no_value"),
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default class ChatChannelSettingsView extends Component {
|
|
||||||
@service chat;
|
|
||||||
@service chatApi;
|
|
||||||
@service chatGuardian;
|
|
||||||
@service currentUser;
|
|
||||||
@service siteSettings;
|
|
||||||
@service router;
|
|
||||||
@service dialog;
|
|
||||||
@service modal;
|
|
||||||
|
|
||||||
notificationLevels = NOTIFICATION_LEVELS;
|
|
||||||
mutedOptions = MUTED_OPTIONS;
|
|
||||||
threadingEnabledOptions = THREADING_ENABLED_OPTIONS;
|
|
||||||
autoAddUsersOptions = AUTO_ADD_USERS_OPTIONS;
|
|
||||||
channelWideMentionsOptions = CHANNEL_WIDE_MENTIONS_OPTIONS;
|
|
||||||
isSavingNotificationSetting = false;
|
|
||||||
savedDesktopNotificationLevel = false;
|
|
||||||
savedMobileNotificationLevel = false;
|
|
||||||
savedMuted = false;
|
|
||||||
|
|
||||||
get togglingChannelWideMentionsAvailable() {
|
|
||||||
return this.args.channel.isCategoryChannel;
|
|
||||||
}
|
|
||||||
|
|
||||||
get autoJoinAvailable() {
|
|
||||||
return (
|
|
||||||
this.siteSettings.max_chat_auto_joined_users > 0 &&
|
|
||||||
this.args.channel.isCategoryChannel
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get adminSectionAvailable() {
|
|
||||||
return (
|
|
||||||
this.chatGuardian.canEditChatChannel() &&
|
|
||||||
(this.autoJoinAvailable || this.togglingChannelWideMentionsAvailable)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get canArchiveChannel() {
|
|
||||||
return (
|
|
||||||
this.siteSettings.chat_allow_archiving_channels &&
|
|
||||||
!this.args.channel.isArchived &&
|
|
||||||
!this.args.channel.isReadOnly
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
saveNotificationSettings(frontendKey, backendKey, newValue) {
|
|
||||||
if (this.args.channel.currentUserMembership[frontendKey] === newValue) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const settings = {};
|
|
||||||
settings[backendKey] = newValue;
|
|
||||||
return this.chatApi
|
|
||||||
.updateCurrentUserChannelNotificationsSettings(
|
|
||||||
this.args.channel.id,
|
|
||||||
settings
|
|
||||||
)
|
|
||||||
.then((result) => {
|
|
||||||
this.args.channel.currentUserMembership[frontendKey] =
|
|
||||||
result.membership[backendKey];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
onArchiveChannel() {
|
|
||||||
return this.modal.show(ChatModalArchiveChannel, {
|
|
||||||
model: { channel: this.args.channel },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
onDeleteChannel() {
|
|
||||||
return this.modal.show(ChatModalDeleteChannel, {
|
|
||||||
model: { channel: this.args.channel },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
onToggleChannelState() {
|
|
||||||
this.modal.show(ChatModalToggleChannelStatus, { model: this.args.channel });
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
onToggleAutoJoinUsers() {
|
|
||||||
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.args.channel.allowChannelWideMentions;
|
|
||||||
if (this.args.channel.allowChannelWideMentions === newValue) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._updateChannelProperty(
|
|
||||||
this.args.channel,
|
|
||||||
"allow_channel_wide_mentions",
|
|
||||||
newValue
|
|
||||||
).then((result) => {
|
|
||||||
this.args.channel.allowChannelWideMentions =
|
|
||||||
result.channel.allow_channel_wide_mentions;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onDisableAutoJoinUsers() {
|
|
||||||
if (this.args.channel.autoJoinUsers === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._updateChannelProperty(
|
|
||||||
this.args.channel,
|
|
||||||
"auto_join_users",
|
|
||||||
false
|
|
||||||
).then((result) => {
|
|
||||||
this.args.channel.autoJoinUsers = result.channel.auto_join_users;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onEnableAutoJoinUsers() {
|
|
||||||
if (this.args.channel.autoJoinUsers === true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dialog.confirm({
|
|
||||||
message: I18n.t("chat.settings.auto_join_users_warning", {
|
|
||||||
category: this.args.channel.chatable.name,
|
|
||||||
}),
|
|
||||||
didConfirm: () =>
|
|
||||||
this._updateChannelProperty(
|
|
||||||
this.args.channel,
|
|
||||||
"auto_join_users",
|
|
||||||
true
|
|
||||||
).then((result) => {
|
|
||||||
this.args.channel.autoJoinUsers = result.channel.auto_join_users;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateChannelProperty(channel, property, value) {
|
|
||||||
const payload = {};
|
|
||||||
payload[property] = value;
|
|
||||||
|
|
||||||
return this.chatApi.updateChannel(channel.id, payload).catch((event) => {
|
|
||||||
if (event.jqXHR?.responseJSON?.errors) {
|
|
||||||
this.flash(event.jqXHR.responseJSON.errors.join("\n"), "error");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,581 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import ChatForm from "discourse/plugins/chat/discourse/components/chat/form";
|
||||||
|
import DToggleSwitch from "discourse/components/d-toggle-switch";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
import ChatModalArchiveChannel from "discourse/plugins/chat/discourse/components/chat/modal/archive-channel";
|
||||||
|
import ChatModalDeleteChannel from "discourse/plugins/chat/discourse/components/chat/modal/delete-channel";
|
||||||
|
import ChatModalToggleChannelStatus from "discourse/plugins/chat/discourse/components/chat/modal/toggle-channel-status";
|
||||||
|
import { on } from "@ember/modifier";
|
||||||
|
import I18n from "I18n";
|
||||||
|
import { fn, hash } from "@ember/helper";
|
||||||
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
import ComboBox from "select-kit/components/combo-box";
|
||||||
|
import ChatRetentionReminderText from "discourse/plugins/chat/discourse/components/chat-retention-reminder-text";
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import ToggleChannelMembershipButton from "discourse/plugins/chat/discourse/components/toggle-channel-membership-button";
|
||||||
|
import replaceEmoji from "discourse/helpers/replace-emoji";
|
||||||
|
import ChatModalEditChannelName from "discourse/plugins/chat/discourse/components/chat/modal/edit-channel-name";
|
||||||
|
import categoryBadge from "discourse/helpers/category-badge";
|
||||||
|
import ChatModalEditChannelDescription from "discourse/plugins/chat/discourse/components/chat/modal/edit-channel-description";
|
||||||
|
import { LinkTo } from "@ember/routing";
|
||||||
|
|
||||||
|
const NOTIFICATION_LEVELS = [
|
||||||
|
{ name: I18n.t("chat.notification_levels.never"), value: "never" },
|
||||||
|
{ name: I18n.t("chat.notification_levels.mention"), value: "mention" },
|
||||||
|
{ name: I18n.t("chat.notification_levels.always"), value: "always" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default class ChatAboutScreen extends Component {
|
||||||
|
<template>
|
||||||
|
<div class="chat-channel-settings">
|
||||||
|
<ChatForm as |form|>
|
||||||
|
{{#if this.shouldRenderTitleSection}}
|
||||||
|
<form.section @title={{this.titleSectionTitle}} as |section|>
|
||||||
|
<section.row>
|
||||||
|
<:default>
|
||||||
|
<div class="chat-channel-settings__name">
|
||||||
|
{{replaceEmoji @channel.title}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if @channel.isCategoryChannel}}
|
||||||
|
<div class="chat-channel-settings__slug">
|
||||||
|
<LinkTo
|
||||||
|
@route="chat.channel"
|
||||||
|
@models={{@channel.routeModels}}
|
||||||
|
>
|
||||||
|
/chat/c/{{@channel.slug}}/{{@channel.id}}
|
||||||
|
</LinkTo>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</:default>
|
||||||
|
|
||||||
|
<:action>
|
||||||
|
{{#if this.canEditChannel}}
|
||||||
|
<DButton
|
||||||
|
@label="chat.channel_settings.edit"
|
||||||
|
@action={{this.onEditChannelName}}
|
||||||
|
class="edit-name-slug-btn btn-flat"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</:action>
|
||||||
|
|
||||||
|
</section.row>
|
||||||
|
</form.section>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.shouldRenderDescriptionSection}}
|
||||||
|
<form.section @title={{this.descriptionSectionTitle}} as |section|>
|
||||||
|
<section.row>
|
||||||
|
<:default>
|
||||||
|
{{#if @channel.description.length}}
|
||||||
|
{{@channel.description}}
|
||||||
|
{{else}}
|
||||||
|
{{this.descriptionPlaceholder}}
|
||||||
|
{{/if}}
|
||||||
|
</:default>
|
||||||
|
|
||||||
|
<:action>
|
||||||
|
{{#if this.canEditChannel}}
|
||||||
|
<DButton
|
||||||
|
@label={{if
|
||||||
|
@channel.description.length
|
||||||
|
"chat.channel_settings.edit"
|
||||||
|
"chat.channel_settings.add"
|
||||||
|
}}
|
||||||
|
@action={{this.onEditChannelDescription}}
|
||||||
|
class="edit-description-btn btn-flat"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</:action>
|
||||||
|
</section.row>
|
||||||
|
</form.section>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.site.mobileView}}
|
||||||
|
<form.section as |section|>
|
||||||
|
<section.row
|
||||||
|
@label={{this.membersLabel}}
|
||||||
|
@route="chat.channel.info.members"
|
||||||
|
@routeModels={{@channel.routeModels}}
|
||||||
|
/>
|
||||||
|
</form.section>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if @channel.isOpen}}
|
||||||
|
<form.section @title={{this.settingsSectionTitle}} as |section|>
|
||||||
|
<section.row @label={{this.muteSectionLabel}}>
|
||||||
|
<:action>
|
||||||
|
<DToggleSwitch
|
||||||
|
@state={{@channel.currentUserMembership.muted}}
|
||||||
|
class="chat-channel-settings__mute-switch"
|
||||||
|
{{on "click" this.onToggleMuted}}
|
||||||
|
/>
|
||||||
|
</:action>
|
||||||
|
</section.row>
|
||||||
|
|
||||||
|
{{#if this.shouldRenderDesktopNotificationsLevelSection}}
|
||||||
|
<section.row @label={{this.desktopNotificationsLevelLabel}}>
|
||||||
|
<:action>
|
||||||
|
<ComboBox
|
||||||
|
@content={{this.notificationLevels}}
|
||||||
|
@value={{@channel.currentUserMembership.desktopNotificationLevel}}
|
||||||
|
@valueProperty="value"
|
||||||
|
@class="chat-channel-settings__selector chat-channel-settings__desktop-notifications-selector"
|
||||||
|
@onChange={{fn
|
||||||
|
this.saveNotificationSettings
|
||||||
|
"desktopNotificationLevel"
|
||||||
|
"desktop_notification_level"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</:action>
|
||||||
|
</section.row>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.shouldRenderMobileNotificationsLevelSection}}
|
||||||
|
<section.row @label={{this.mobileNotificationsLevelLabel}}>
|
||||||
|
<:action>
|
||||||
|
<ComboBox
|
||||||
|
@content={{this.notificationLevels}}
|
||||||
|
@value={{@channel.currentUserMembership.mobileNotificationLevel}}
|
||||||
|
@valueProperty="value"
|
||||||
|
@class="chat-channel-settings__selector chat-channel-settings__mobile-notifications-selector"
|
||||||
|
@onChange={{fn
|
||||||
|
this.saveNotificationSettings
|
||||||
|
"mobileNotificationLevel"
|
||||||
|
"mobile_notification_level"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</:action>
|
||||||
|
</section.row>
|
||||||
|
{{/if}}
|
||||||
|
</form.section>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<form.section @title={{this.channelInfoSectionTitle}} as |section|>
|
||||||
|
{{#if @channel.isCategoryChannel}}
|
||||||
|
<section.row @label={{this.categoryLabel}}>
|
||||||
|
{{categoryBadge
|
||||||
|
@channel.chatable
|
||||||
|
link=true
|
||||||
|
allowUncategorized=true
|
||||||
|
}}
|
||||||
|
</section.row>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<section.row @label={{this.historyLabel}}>
|
||||||
|
<ChatRetentionReminderText @channel={{@channel}} />
|
||||||
|
</section.row>
|
||||||
|
</form.section>
|
||||||
|
|
||||||
|
{{#if this.shouldRenderAdminSection}}
|
||||||
|
<form.section
|
||||||
|
@title={{this.adminSectionTitle}}
|
||||||
|
data-section="admin"
|
||||||
|
as |section|
|
||||||
|
>
|
||||||
|
{{#if this.autoJoinAvailable}}
|
||||||
|
<section.row @label={{this.autoJoinLabel}}>
|
||||||
|
<:action>
|
||||||
|
<DToggleSwitch
|
||||||
|
@state={{@channel.autoJoinUsers}}
|
||||||
|
class="chat-channel-settings__auto-join-switch"
|
||||||
|
{{on
|
||||||
|
"click"
|
||||||
|
(fn this.onToggleAutoJoinUsers @channel.autoJoinUsers)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</:action>
|
||||||
|
</section.row>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.toggleChannelWideMentionsAvailable}}
|
||||||
|
<section.row @label={{this.channelWideMentionsLabel}}>
|
||||||
|
<:action>
|
||||||
|
<DToggleSwitch
|
||||||
|
class="chat-channel-settings__channel-wide-mentions"
|
||||||
|
@state={{@channel.allowChannelWideMentions}}
|
||||||
|
{{on
|
||||||
|
"click"
|
||||||
|
(fn
|
||||||
|
this.onToggleChannelWideMentions
|
||||||
|
@channel.allowChannelWideMentions
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</:action>
|
||||||
|
|
||||||
|
<:description>
|
||||||
|
{{this.channelWideMentionsDescription}}
|
||||||
|
</:description>
|
||||||
|
</section.row>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.toggleThreadingAvailable}}
|
||||||
|
<section.row @label={{this.toggleThreadingLabel}}>
|
||||||
|
<:action>
|
||||||
|
<DToggleSwitch
|
||||||
|
@state={{@channel.threadingEnabled}}
|
||||||
|
class="chat-channel-settings__threading-switch"
|
||||||
|
{{on
|
||||||
|
"click"
|
||||||
|
(fn
|
||||||
|
this.onToggleThreadingEnabled @channel.threadingEnabled
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</:action>
|
||||||
|
|
||||||
|
<:description>
|
||||||
|
{{this.toggleThreadingDescription}}
|
||||||
|
</:description>
|
||||||
|
</section.row>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.shouldRenderStatusSection}}
|
||||||
|
{{#if this.shouldRenderArchiveRow}}
|
||||||
|
<section.row>
|
||||||
|
<:action>
|
||||||
|
<DButton
|
||||||
|
@action={{this.onArchiveChannel}}
|
||||||
|
@label="chat.channel_settings.archive_channel"
|
||||||
|
@icon="archive"
|
||||||
|
class="archive-btn chat-form__btn btn-flat"
|
||||||
|
/>
|
||||||
|
</:action>
|
||||||
|
</section.row>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<section.row>
|
||||||
|
<:action>
|
||||||
|
{{#if @channel.isOpen}}
|
||||||
|
<DButton
|
||||||
|
@action={{this.onToggleChannelState}}
|
||||||
|
@label="chat.channel_settings.close_channel"
|
||||||
|
@icon="lock"
|
||||||
|
class="close-btn chat-form__btn btn-flat"
|
||||||
|
/>
|
||||||
|
{{else}}
|
||||||
|
<DButton
|
||||||
|
@action={{this.onToggleChannelState}}
|
||||||
|
@label="chat.channel_settings.open_channel"
|
||||||
|
@icon="unlock"
|
||||||
|
class="open-btn chat-form__btn btn-flat"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</:action>
|
||||||
|
</section.row>
|
||||||
|
|
||||||
|
<section.row>
|
||||||
|
<:action>
|
||||||
|
<DButton
|
||||||
|
@action={{this.onDeleteChannel}}
|
||||||
|
@label="chat.channel_settings.delete_channel"
|
||||||
|
@icon="trash-alt"
|
||||||
|
class="delete-btn chat-form__btn btn-flat"
|
||||||
|
/>
|
||||||
|
</:action>
|
||||||
|
</section.row>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
</form.section>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<form.section as |section|>
|
||||||
|
<section.row>
|
||||||
|
<:action>
|
||||||
|
<ToggleChannelMembershipButton
|
||||||
|
@channel={{@channel}}
|
||||||
|
@options={{hash
|
||||||
|
joinClass="btn-primary"
|
||||||
|
leaveClass="btn-flat"
|
||||||
|
joinIcon="sign-in-alt"
|
||||||
|
leaveIcon="sign-out-alt"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</:action>
|
||||||
|
</section.row>
|
||||||
|
</form.section>
|
||||||
|
</ChatForm>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
@service chatApi;
|
||||||
|
@service chatGuardian;
|
||||||
|
@service currentUser;
|
||||||
|
@service siteSettings;
|
||||||
|
@service dialog;
|
||||||
|
@service modal;
|
||||||
|
@service site;
|
||||||
|
@service toasts;
|
||||||
|
|
||||||
|
notificationLevels = NOTIFICATION_LEVELS;
|
||||||
|
|
||||||
|
settingsSectionTitle = I18n.t("chat.settings.settings_title");
|
||||||
|
channelInfoSectionTitle = I18n.t("chat.settings.info_title");
|
||||||
|
categoryLabel = I18n.t("chat.settings.category_label");
|
||||||
|
historyLabel = I18n.t("chat.settings.history_label");
|
||||||
|
adminSectionTitle = I18n.t("chat.settings.admin_title");
|
||||||
|
membersLabel = I18n.t("chat.settings.tabs.members_label");
|
||||||
|
descriptionSectionTitle = I18n.t("chat.about_view.description");
|
||||||
|
titleSectionTitle = I18n.t("chat.about_view.title");
|
||||||
|
descriptionPlaceholder = I18n.t(
|
||||||
|
"chat.channel_edit_description_modal.description"
|
||||||
|
);
|
||||||
|
toggleThreadingLabel = I18n.t("chat.settings.channel_threading_label");
|
||||||
|
toggleThreadingDescription = I18n.t(
|
||||||
|
"chat.settings.channel_threading_description"
|
||||||
|
);
|
||||||
|
muteSectionLabel = I18n.t("chat.settings.mute");
|
||||||
|
channelWideMentionsLabel = I18n.t(
|
||||||
|
"chat.settings.channel_wide_mentions_label"
|
||||||
|
);
|
||||||
|
autoJoinLabel = I18n.t("chat.settings.auto_join_users_label");
|
||||||
|
desktopNotificationsLevelLabel = I18n.t(
|
||||||
|
"chat.settings.desktop_notification_level"
|
||||||
|
);
|
||||||
|
mobileNotificationsLevelLabel = I18n.t(
|
||||||
|
"chat.settings.mobile_notification_level"
|
||||||
|
);
|
||||||
|
|
||||||
|
get canEditChannel() {
|
||||||
|
return this.chatGuardian.canEditChatChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
get shouldRenderTitleSection() {
|
||||||
|
return this.args.channel.isCategoryChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
get shouldRenderDescriptionSection() {
|
||||||
|
return this.args.channel.isCategoryChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
get shouldRenderStatusSection() {
|
||||||
|
return this.args.channel.isCategoryChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
get shouldRenderArchiveRow() {
|
||||||
|
return this.chatGuardian.canArchiveChannel(this.args.channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
get toggleChannelWideMentionsAvailable() {
|
||||||
|
return this.args.channel.isCategoryChannel && this.args.channel.isOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
get toggleThreadingAvailable() {
|
||||||
|
return this.args.channel.isCategoryChannel && this.args.channel.isOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
get channelWideMentionsDescription() {
|
||||||
|
return I18n.t("chat.settings.channel_wide_mentions_description", {
|
||||||
|
channel: this.args.channel.title,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get isChannelMuted() {
|
||||||
|
return this.args.channel.currentUserMembership.muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
get shouldRenderChannelWideMentionsAvailable() {
|
||||||
|
return this.args.channel.isCategoryChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
get shouldRenderDesktopNotificationsLevelSection() {
|
||||||
|
return !this.isChannelMuted;
|
||||||
|
}
|
||||||
|
|
||||||
|
get shouldRenderMobileNotificationsLevelSection() {
|
||||||
|
return !this.isChannelMuted;
|
||||||
|
}
|
||||||
|
|
||||||
|
get autoJoinAvailable() {
|
||||||
|
return (
|
||||||
|
this.siteSettings.max_chat_auto_joined_users > 0 &&
|
||||||
|
this.args.channel.isCategoryChannel &&
|
||||||
|
this.args.channel.isOpen
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get shouldRenderAdminSection() {
|
||||||
|
return (
|
||||||
|
this.canEditChannel &&
|
||||||
|
(this.toggleChannelWideMentionsAvailable ||
|
||||||
|
this.args.channel.isCategoryChannel)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
async onToggleChannelWideMentions() {
|
||||||
|
const newValue = !this.args.channel.allowChannelWideMentions;
|
||||||
|
|
||||||
|
if (this.args.channel.allowChannelWideMentions === newValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.args.channel.allowChannelWideMentions = newValue;
|
||||||
|
|
||||||
|
const result = await this._updateChannelProperty(
|
||||||
|
this.args.channel,
|
||||||
|
"allow_channel_wide_mentions",
|
||||||
|
newValue
|
||||||
|
);
|
||||||
|
|
||||||
|
this.args.channel.allowChannelWideMentions =
|
||||||
|
result.channel.allow_channel_wide_mentions;
|
||||||
|
} catch (error) {
|
||||||
|
popupAjaxError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
async onToggleAutoJoinUsers() {
|
||||||
|
if (this.args.channel.autoJoinUsers) {
|
||||||
|
return await this.onDisableAutoJoinUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.onEnableAutoJoinUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
async onDisableAutoJoinUsers() {
|
||||||
|
if (this.args.channel.autoJoinUsers === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.args.channel.autoJoinUsers = false;
|
||||||
|
|
||||||
|
const result = await this._updateChannelProperty(
|
||||||
|
this.args.channel,
|
||||||
|
"auto_join_users",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
this.args.channel.autoJoinUsers = result.channel.auto_join_users;
|
||||||
|
} catch (error) {
|
||||||
|
popupAjaxError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onEnableAutoJoinUsers() {
|
||||||
|
if (this.args.channel.autoJoinUsers === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.dialog.confirm({
|
||||||
|
message: I18n.t("chat.settings.auto_join_users_warning", {
|
||||||
|
category: this.args.channel.chatable.name,
|
||||||
|
}),
|
||||||
|
didConfirm: async () => {
|
||||||
|
try {
|
||||||
|
const result = await this._updateChannelProperty(
|
||||||
|
this.args.channel,
|
||||||
|
"auto_join_users",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
this.args.channel.autoJoinUsers = result.channel.auto_join_users;
|
||||||
|
} catch (error) {
|
||||||
|
popupAjaxError(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onToggleMuted() {
|
||||||
|
const newValue = !this.args.channel.currentUserMembership.muted;
|
||||||
|
this.saveNotificationSettings("muted", "muted", newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
async saveNotificationSettings(frontendKey, backendKey, newValue) {
|
||||||
|
if (this.args.channel.currentUserMembership[frontendKey] === newValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.args.channel.currentUserMembership[frontendKey] = newValue;
|
||||||
|
|
||||||
|
const settings = {};
|
||||||
|
settings[backendKey] = newValue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result =
|
||||||
|
await this.chatApi.updateCurrentUserChannelNotificationsSettings(
|
||||||
|
this.args.channel.id,
|
||||||
|
settings
|
||||||
|
);
|
||||||
|
|
||||||
|
this.args.channel.currentUserMembership[frontendKey] =
|
||||||
|
result.membership[backendKey];
|
||||||
|
this.toasts.success({ data: { message: I18n.t("saved") } });
|
||||||
|
} catch (error) {
|
||||||
|
popupAjaxError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
async _updateChannelProperty(channel, property, value) {
|
||||||
|
try {
|
||||||
|
const result = await this.chatApi.updateChannel(channel.id, {
|
||||||
|
[property]: value,
|
||||||
|
});
|
||||||
|
this.toasts.success({ data: { message: I18n.t("saved") } });
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
popupAjaxError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
async onToggleThreadingEnabled(value) {
|
||||||
|
try {
|
||||||
|
this.args.channel.threadingEnabled = !value;
|
||||||
|
const result = await this._updateChannelProperty(
|
||||||
|
this.args.channel,
|
||||||
|
"threading_enabled",
|
||||||
|
!value
|
||||||
|
);
|
||||||
|
this.args.channel.threadingEnabled = result.channel.threading_enabled;
|
||||||
|
} catch (error) {
|
||||||
|
popupAjaxError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onToggleChannelState() {
|
||||||
|
return this.modal.show(ChatModalToggleChannelStatus, {
|
||||||
|
model: this.args.channel,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onArchiveChannel() {
|
||||||
|
return this.modal.show(ChatModalArchiveChannel, {
|
||||||
|
model: { channel: this.args.channel },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onDeleteChannel() {
|
||||||
|
return this.modal.show(ChatModalDeleteChannel, {
|
||||||
|
model: { channel: this.args.channel },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onEditChannelName() {
|
||||||
|
return this.modal.show(ChatModalEditChannelName, {
|
||||||
|
model: this.args.channel,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
onEditChannelDescription() {
|
||||||
|
return this.modal.show(ChatModalEditChannelDescription, {
|
||||||
|
model: this.args.channel,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,12 @@ import I18n from "I18n";
|
|||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
|
|
||||||
export default class ChatRetentionReminderText extends Component {
|
export default class ChatRetentionReminderText extends Component {
|
||||||
|
<template>
|
||||||
|
<span class="chat-retention-reminder-text">
|
||||||
|
{{this.text}}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
@service siteSettings;
|
@service siteSettings;
|
||||||
|
|
||||||
get text() {
|
get text() {
|
@ -1,3 +0,0 @@
|
|||||||
<span class="chat-retention-reminder-text">
|
|
||||||
{{this.text}}
|
|
||||||
</span>
|
|
@ -0,0 +1,25 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { userPath } from "discourse/lib/url";
|
||||||
|
import ChatUserAvatar from "discourse/plugins/chat/discourse/components/chat/user-avatar";
|
||||||
|
import ChatUserDisplayName from "discourse/plugins/chat/discourse/components/chat-user-display-name";
|
||||||
|
|
||||||
|
export default class ChatUserInfo extends Component {
|
||||||
|
<template>
|
||||||
|
{{#if @user}}
|
||||||
|
<a href={{this.userPath}} data-user-card={{@user.username}}>
|
||||||
|
<ChatUserAvatar @user={{@user}} @avatarSize={{this.avatarSize}} />
|
||||||
|
</a>
|
||||||
|
<a href={{this.userPath}} data-user-card={{@user.username}}>
|
||||||
|
<ChatUserDisplayName @user={{@user}} />
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
get avatarSize() {
|
||||||
|
return this.args.avatarSize ?? "medium";
|
||||||
|
}
|
||||||
|
|
||||||
|
get userPath() {
|
||||||
|
return userPath(this.args.user.username);
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +0,0 @@
|
|||||||
{{#if @user}}
|
|
||||||
<a href={{this.userPath}} data-user-card={{@user.username}}>
|
|
||||||
<Chat::UserAvatar @user={{@user}} @avatarSize="medium" />
|
|
||||||
</a>
|
|
||||||
<a href={{this.userPath}} data-user-card={{@user.username}}>
|
|
||||||
<ChatUserDisplayName @user={{@user}} />
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
@ -1,8 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
import { userPath } from "discourse/lib/url";
|
|
||||||
|
|
||||||
export default class ChatUserInfo extends Component {
|
|
||||||
get userPath() {
|
|
||||||
return userPath(this.args.user.username);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,14 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import ChatFormSection from "discourse/plugins/chat/discourse/components/chat/form/section";
|
||||||
|
|
||||||
|
export default class ChatForm extends Component {
|
||||||
|
<template>
|
||||||
|
<div class="chat-form">
|
||||||
|
{{yield this.yieldableArgs}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
get yieldableArgs() {
|
||||||
|
return { section: ChatFormSection };
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { LinkTo } from "@ember/routing";
|
||||||
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
|
import concatClass from "discourse/helpers/concat-class";
|
||||||
|
|
||||||
|
export default class ChatFormRow extends Component {
|
||||||
|
<template>
|
||||||
|
{{#if @route}}
|
||||||
|
<LinkTo
|
||||||
|
@route={{@route}}
|
||||||
|
@models={{@routeModels}}
|
||||||
|
class={{concatClass
|
||||||
|
"chat-form__row -link"
|
||||||
|
(if @separator "-separator")
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class="chat-form__row-content">
|
||||||
|
{{@label}}
|
||||||
|
{{icon "chevron-right" class="chat-form__row-icon"}}
|
||||||
|
</div>
|
||||||
|
</LinkTo>
|
||||||
|
{{else}}
|
||||||
|
<div class={{concatClass "chat-form__row" (if @separator "-separator")}}>
|
||||||
|
<div class="chat-form__row-content">
|
||||||
|
{{#if @label}}
|
||||||
|
<span class="chat-form__row-label">{{@label}}</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if (has-block)}}
|
||||||
|
<span class="chat-form__row-label">
|
||||||
|
{{yield}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if (has-block "action")}}
|
||||||
|
<div class="chat-form__row-action">{{yield to="action"}}</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if (has-block "description")}}
|
||||||
|
<div class="chat-form__row-description">
|
||||||
|
{{yield to="description"}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import ChatFormRow from "discourse/plugins/chat/discourse/components/chat/form/row";
|
||||||
|
|
||||||
|
export default class ChatFormSection extends Component {
|
||||||
|
<template>
|
||||||
|
<div class="chat-form__section" ...attributes>
|
||||||
|
{{#if @title}}
|
||||||
|
<div class="chat-form__section-title">
|
||||||
|
{{@title}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="chat-form__section-content">
|
||||||
|
{{yield this.yieldableArgs}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
get yieldableArgs() {
|
||||||
|
return { row: ChatFormRow };
|
||||||
|
}
|
||||||
|
}
|
@ -25,12 +25,10 @@
|
|||||||
<div class="edit-channel-control">
|
<div class="edit-channel-control">
|
||||||
<label for="channel-slug" class="edit-channel-label">
|
<label for="channel-slug" class="edit-channel-label">
|
||||||
{{i18n "chat.channel_edit_name_slug_modal.slug"}}
|
{{i18n "chat.channel_edit_name_slug_modal.slug"}}
|
||||||
<span>
|
<DTooltip
|
||||||
{{d-icon "info-circle"}}
|
@icon="info-circle"
|
||||||
<DTooltip>{{i18n
|
@content={{i18n "chat.channel_edit_name_slug_modal.slug_description"}}
|
||||||
"chat.channel_edit_name_slug_modal.slug_description"
|
/>
|
||||||
}}</DTooltip>
|
|
||||||
</span>
|
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
name="channel-slug"
|
name="channel-slug"
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { Input } from "@ember/component";
|
||||||
|
import { on } from "@ember/modifier";
|
||||||
|
import noop from "discourse/helpers/noop";
|
||||||
|
import concatClass from "discourse/helpers/concat-class";
|
||||||
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
|
import { modifier } from "ember-modifier";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
|
||||||
|
export default class DcFilterInput extends Component {
|
||||||
|
<template>
|
||||||
|
{{! template-lint-disable modifier-name-case }}
|
||||||
|
<div
|
||||||
|
class={{concatClass
|
||||||
|
@class
|
||||||
|
"dc-filter-input-container"
|
||||||
|
(if this.isFocused " is-focused")
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{{#if @icons.left}}
|
||||||
|
{{icon @icons.left class="-left"}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<Input
|
||||||
|
class="dc-filter-input"
|
||||||
|
@value={{@value}}
|
||||||
|
{{on "input" (if @filterAction @filterAction (noop))}}
|
||||||
|
{{this.focusState}}
|
||||||
|
...attributes
|
||||||
|
/>
|
||||||
|
|
||||||
|
{{yield}}
|
||||||
|
|
||||||
|
{{#if @icons.right}}
|
||||||
|
{{icon @icons.right class="-right"}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
@tracked isFocused = false;
|
||||||
|
|
||||||
|
focusState = modifier((element) => {
|
||||||
|
const focusInHandler = () => {
|
||||||
|
this.isFocused = true;
|
||||||
|
};
|
||||||
|
const focusOutHandler = () => {
|
||||||
|
this.isFocused = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
element.addEventListener("focusin", focusInHandler);
|
||||||
|
element.addEventListener("focusout", focusOutHandler);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
element.removeEventListener("focusin", focusInHandler);
|
||||||
|
element.removeEventListener("focusout", focusOutHandler);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
@ -1,26 +0,0 @@
|
|||||||
<div
|
|
||||||
class={{concat
|
|
||||||
@class
|
|
||||||
" dc-filter-input-container"
|
|
||||||
(if this.isFocused " is-focused")
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{{#if @icons.left}}
|
|
||||||
{{d-icon @icons.left class="-left"}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<Input
|
|
||||||
class="dc-filter-input"
|
|
||||||
@value={{@value}}
|
|
||||||
{{on "input" (if @filterAction @filterAction (noop))}}
|
|
||||||
{{on "focusin" (action (mut this.isFocused) true)}}
|
|
||||||
{{on "focusout" (action (mut this.isFocused) false)}}
|
|
||||||
...attributes
|
|
||||||
/>
|
|
||||||
|
|
||||||
{{yield}}
|
|
||||||
|
|
||||||
{{#if @icons.right}}
|
|
||||||
{{d-icon @icons.right class="-right"}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
@ -1,3 +0,0 @@
|
|||||||
import Component from "@glimmer/component";
|
|
||||||
|
|
||||||
export default class DcFilterInput extends Component {}
|
|
@ -1,26 +0,0 @@
|
|||||||
import Controller from "@ember/controller";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
|
||||||
import { inject as service } from "@ember/service";
|
|
||||||
import ChatModalEditChannelDescription from "discourse/plugins/chat/discourse/components/chat/modal/edit-channel-description";
|
|
||||||
import ChatModalEditChannelName from "discourse/plugins/chat/discourse/components/chat/modal/edit-channel-name";
|
|
||||||
|
|
||||||
export default class ChatChannelInfoAboutController extends Controller.extend(
|
|
||||||
ModalFunctionality
|
|
||||||
) {
|
|
||||||
@service modal;
|
|
||||||
|
|
||||||
@action
|
|
||||||
onEditChatChannelName() {
|
|
||||||
return this.modal.show(ChatModalEditChannelName, {
|
|
||||||
model: this.model,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
onEditChatChannelDescription() {
|
|
||||||
return this.modal.show(ChatModalEditChannelDescription, {
|
|
||||||
model: this.model,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
import Controller from "@ember/controller";
|
|
||||||
|
|
||||||
export default class ChatChannelInfoMembersController extends Controller {}
|
|
@ -1,3 +0,0 @@
|
|||||||
import Controller from "@ember/controller";
|
|
||||||
|
|
||||||
export default class ChatChannelInfoSettingsController extends Controller {}
|
|
@ -1,34 +0,0 @@
|
|||||||
import Controller from "@ember/controller";
|
|
||||||
import { inject as service } from "@ember/service";
|
|
||||||
import { reads } from "@ember/object/computed";
|
|
||||||
import { computed } from "@ember/object";
|
|
||||||
|
|
||||||
export default class ChatChannelInfoIndexController extends Controller {
|
|
||||||
@service router;
|
|
||||||
@service chat;
|
|
||||||
@service chatChannelInfoRouteOriginManager;
|
|
||||||
|
|
||||||
@reads("router.currentRoute.localName") tab;
|
|
||||||
|
|
||||||
@computed("model.{membershipsCount,status,currentUserMembership.following}")
|
|
||||||
get tabs() {
|
|
||||||
const tabs = [];
|
|
||||||
|
|
||||||
if (!this.model.isDirectMessageChannel) {
|
|
||||||
tabs.push("about");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.model.isOpen && this.model.membershipsCount >= 1) {
|
|
||||||
tabs.push("members");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
this.currentUser?.staff ||
|
|
||||||
this.model.currentUserMembership?.following
|
|
||||||
) {
|
|
||||||
tabs.push("settings");
|
|
||||||
}
|
|
||||||
|
|
||||||
return tabs;
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,9 +12,10 @@ export default class Collection {
|
|||||||
@tracked loading = false;
|
@tracked loading = false;
|
||||||
@tracked fetchedOnce = false;
|
@tracked fetchedOnce = false;
|
||||||
|
|
||||||
constructor(resourceURL, handler) {
|
constructor(resourceURL, handler, params = {}) {
|
||||||
this._resourceURL = resourceURL;
|
this._resourceURL = resourceURL;
|
||||||
this._handler = handler;
|
this._handler = handler;
|
||||||
|
this._params = params;
|
||||||
this._fetchedAll = false;
|
this._fetchedAll = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +95,6 @@ export default class Collection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#fetch(url) {
|
#fetch(url) {
|
||||||
return ajax(url, { type: "GET" });
|
return ajax(url, { type: "GET", data: this._params });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
import DiscourseRoute from "discourse/routes/discourse";
|
|
||||||
import { inject as service } from "@ember/service";
|
|
||||||
|
|
||||||
export default class ChatChannelInfoAboutRoute extends DiscourseRoute {
|
|
||||||
@service router;
|
|
||||||
|
|
||||||
afterModel(model) {
|
|
||||||
if (model.isDirectMessageChannel) {
|
|
||||||
this.router.replaceWith("chat.channel.info.index");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,15 +4,7 @@ import { inject as service } from "@ember/service";
|
|||||||
export default class ChatChannelInfoIndexRoute extends DiscourseRoute {
|
export default class ChatChannelInfoIndexRoute extends DiscourseRoute {
|
||||||
@service router;
|
@service router;
|
||||||
|
|
||||||
afterModel(model) {
|
afterModel() {
|
||||||
if (model.isDirectMessageChannel) {
|
|
||||||
if (model.isOpen && model.membershipsCount >= 1) {
|
|
||||||
this.router.replaceWith("chat.channel.info.members");
|
|
||||||
} else {
|
|
||||||
this.router.replaceWith("chat.channel.info.settings");
|
this.router.replaceWith("chat.channel.info.settings");
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.router.replaceWith("chat.channel.info.about");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,8 @@ export default class ChatChannelInfoMembersRoute extends DiscourseRoute {
|
|||||||
@service router;
|
@service router;
|
||||||
|
|
||||||
afterModel(model) {
|
afterModel(model) {
|
||||||
if (!model.isOpen) {
|
if (!model.isOpen || model.membershipsCount < 1) {
|
||||||
return this.router.replaceWith("chat.channel.info.settings");
|
return this.router.replaceWith("chat.channel.info.settings");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.membershipsCount < 1) {
|
|
||||||
return this.router.replaceWith("chat.channel.info");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
import DiscourseRoute from "discourse/routes/discourse";
|
|
||||||
import { inject as service } from "@ember/service";
|
|
||||||
|
|
||||||
export default class ChatChannelInfoSettingsRoute extends DiscourseRoute {
|
|
||||||
@service router;
|
|
||||||
@service currentUser;
|
|
||||||
|
|
||||||
afterModel(model) {
|
|
||||||
if (!this.currentUser?.staff && !model.currentUserMembership?.following) {
|
|
||||||
this.router.replaceWith("chat.channel.info");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -233,14 +233,15 @@ export default class ChatApi extends Service {
|
|||||||
* @param {number} channelId - The ID of the channel.
|
* @param {number} channelId - The ID of the channel.
|
||||||
* @returns {Collection}
|
* @returns {Collection}
|
||||||
*/
|
*/
|
||||||
listChannelMemberships(channelId) {
|
listChannelMemberships(channelId, params = {}) {
|
||||||
return new Collection(
|
return new Collection(
|
||||||
`${this.#basePath}/channels/${channelId}/memberships`,
|
`${this.#basePath}/channels/${channelId}/memberships`,
|
||||||
(response) => {
|
(response) => {
|
||||||
return response.memberships.map((membership) =>
|
return response.memberships.map((membership) =>
|
||||||
UserChatChannelMembership.create(membership)
|
UserChatChannelMembership.create(membership)
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
params
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ export default class ChatChannelsManager extends Service {
|
|||||||
@service chatSubscriptionsManager;
|
@service chatSubscriptionsManager;
|
||||||
@service chatApi;
|
@service chatApi;
|
||||||
@service currentUser;
|
@service currentUser;
|
||||||
|
@service router;
|
||||||
@tracked _cached = new TrackedObject();
|
@tracked _cached = new TrackedObject();
|
||||||
|
|
||||||
async find(id, options = { fetchIfNotFound: true }) {
|
async find(id, options = { fetchIfNotFound: true }) {
|
||||||
@ -131,12 +132,12 @@ export default class ChatChannelsManager extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async #find(id) {
|
async #find(id) {
|
||||||
return this.chatApi
|
try {
|
||||||
.channel(id)
|
const result = await this.chatApi.channel(id);
|
||||||
.catch(popupAjaxError)
|
|
||||||
.then((result) => {
|
|
||||||
return this.store(result.channel);
|
return this.store(result.channel);
|
||||||
});
|
} catch (error) {
|
||||||
|
popupAjaxError(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#cache(channel) {
|
#cache(channel) {
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import Service from "@ember/service";
|
import Service, { inject as service } from "@ember/service";
|
||||||
|
|
||||||
export default class ChatGuardian extends Service {
|
export default class ChatGuardian extends Service {
|
||||||
|
@service currentUser;
|
||||||
|
@service siteSettings;
|
||||||
|
|
||||||
canEditChatChannel() {
|
canEditChatChannel() {
|
||||||
return this.canUseChat() && this.currentUser.staff;
|
return this.canUseChat() && this.currentUser.staff;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
<ChatChannelAboutView
|
|
||||||
@channel={{this.model}}
|
|
||||||
@onEditChatChannelName={{action "onEditChatChannelName"}}
|
|
||||||
@onEditChatChannelDescription={{action "onEditChatChannelDescription"}}
|
|
||||||
/>
|
|
@ -1 +1 @@
|
|||||||
<ChatChannelMembersView @channel={{this.model}} />
|
<ChatChannelMembers @channel={{this.model}} />
|
@ -1 +1 @@
|
|||||||
<ChatChannelSettingsView @channel={{this.model}} />
|
<ChatChannelSettings @channel={{this.model}} />
|
@ -1,65 +1 @@
|
|||||||
<div class="channel-info">
|
<ChatChannelInfo @channel={{this.model}} />
|
||||||
<div class="chat-full-page-header">
|
|
||||||
<div class="chat-channel-header-details">
|
|
||||||
<div class="chat-full-page-header__left-actions">
|
|
||||||
{{#if this.chatChannelInfoRouteOriginManager.isBrowse}}
|
|
||||||
<LinkTo
|
|
||||||
@route="chat.browse"
|
|
||||||
class="chat-full-page-header__back-btn no-text btn-flat btn"
|
|
||||||
title={{i18n "chat.channel_info.back_to_all_channel"}}
|
|
||||||
>
|
|
||||||
{{d-icon "chevron-left"}}
|
|
||||||
</LinkTo>
|
|
||||||
{{else}}
|
|
||||||
<LinkTo
|
|
||||||
@route="chat.channel"
|
|
||||||
@models={{this.model.routeModels}}
|
|
||||||
class="chat-full-page-header__back-btn no-text btn-flat btn"
|
|
||||||
title={{i18n "chat.channel_info.back_to_channel"}}
|
|
||||||
>
|
|
||||||
{{d-icon "chevron-left"}}
|
|
||||||
</LinkTo>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ChatChannelTitle @channel={{this.model}} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ChatChannelStatus @channel={{this.model}} />
|
|
||||||
|
|
||||||
<div class="chat-tabs chat-info-tabs">
|
|
||||||
<ul class="chat-tabs-list nav-pills" role="tablist">
|
|
||||||
{{#each this.tabs as |tab|}}
|
|
||||||
<li
|
|
||||||
class="chat-tabs-list__item"
|
|
||||||
role="tab"
|
|
||||||
aria-controls={{concat tab "-tab"}}
|
|
||||||
>
|
|
||||||
<LinkTo
|
|
||||||
@route={{concat "chat.channel.info." tab}}
|
|
||||||
@models={{this.model.routeModels}}
|
|
||||||
class="chat-tabs-list__link"
|
|
||||||
>
|
|
||||||
<span>{{i18n (concat "chat.channel_info.tabs." tab)}}</span>
|
|
||||||
{{#if (eq tab "members")}}
|
|
||||||
<span class="chat-tabs__memberships-count">
|
|
||||||
({{this.model.membershipsCount}})
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
</LinkTo>
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div
|
|
||||||
id={{this.tab}}
|
|
||||||
class="chat-tabs__tabpanel"
|
|
||||||
aria-hidden={{notEq this.tab this.activeTab}}
|
|
||||||
role="tabpanel"
|
|
||||||
aria-labelledby={{concat this.tab "-tab"}}
|
|
||||||
>
|
|
||||||
{{outlet}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,11 +1,19 @@
|
|||||||
.channel-info {
|
.chat-channel-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
&__nav {
|
||||||
|
.nav-pills {
|
||||||
|
margin: 0;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info header
|
// Info header
|
||||||
.channel-info-header {
|
.chat-channel-info-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
@ -13,129 +21,7 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.channel-info-header__title {
|
.chat-channel-info-header__title {
|
||||||
font-size: var(--font-up-2);
|
font-size: var(--font-up-2);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// About view
|
|
||||||
.channel-info-about-view__title-input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-info-about-view__description-input {
|
|
||||||
height: 150px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-info-about-view__description__helper-text {
|
|
||||||
color: var(--primary-medium);
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-info-about-view__slug {
|
|
||||||
color: var(--primary-medium);
|
|
||||||
font-size: var(--font-down-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Members list
|
|
||||||
.chat-tabs__memberships-count {
|
|
||||||
margin-left: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-members-view-wrapper {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-members-view__search-input-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
border: 1px solid var(--primary-medium);
|
|
||||||
|
|
||||||
&.is-focused {
|
|
||||||
border: 1px solid var(--tertiary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.d-icon {
|
|
||||||
padding: 0.5rem;
|
|
||||||
color: var(--primary-medium);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input.channel-members-view__search-input {
|
|
||||||
border: 0;
|
|
||||||
margin: 0;
|
|
||||||
outline: 0;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
border: 0;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-members-view__status {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-members-view__list-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-top: 1em;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-members-view__list-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0.5rem 0 0.5rem 1px;
|
|
||||||
|
|
||||||
&:not(:last-child) {
|
|
||||||
border-bottom: 1px solid var(--primary-low);
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-user-avatar {
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Channel info edit name and slug modal
|
|
||||||
.chat-channel-edit-name-slug-modal {
|
|
||||||
.modal-inner-container {
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__name-input,
|
|
||||||
&__slug-input {
|
|
||||||
display: flex;
|
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-channel-edit-name-slug-modal__description {
|
|
||||||
display: flex;
|
|
||||||
padding: 0.5rem 0;
|
|
||||||
color: var(--primary-medium);
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
.chat-channel-members {
|
||||||
|
width: 50%;
|
||||||
|
min-width: 320px;
|
||||||
|
|
||||||
|
&__filter {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__list {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
list-style: none;
|
||||||
|
border-bottom: 1px solid var(--primary-low);
|
||||||
|
height: 42px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&.-no-results {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +0,0 @@
|
|||||||
.chat-channel-settings-saved-indicator {
|
|
||||||
padding-left: 0.5rem;
|
|
||||||
color: var(--success);
|
|
||||||
font-weight: normal;
|
|
||||||
|
|
||||||
.d-icon-check {
|
|
||||||
margin-right: 0.25rem;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,18 @@
|
|||||||
|
.chat-channel-settings {
|
||||||
|
width: 50%;
|
||||||
|
min-width: 320px;
|
||||||
|
|
||||||
|
.chat-channel-settings__slug {
|
||||||
|
max-width: 250px;
|
||||||
|
@include ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
// category badge margin reset
|
||||||
|
.badge-wrapper.bullet {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-retention-reminder-text {
|
||||||
|
color: var(--primary-medium);
|
||||||
|
}
|
||||||
|
}
|
@ -1,62 +1,86 @@
|
|||||||
.chat-form__section {
|
.chat-form {
|
||||||
margin: 1.5rem 1rem;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.chat-form__section-admin-title {
|
|
||||||
margin-inline: 1rem;
|
|
||||||
padding-top: 1rem;
|
|
||||||
border-top: 1px solid var(--primary-low);
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-form__field {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-form__description {
|
|
||||||
margin-top: 3px;
|
|
||||||
color: var(--primary-medium);
|
|
||||||
font-size: var(--font-down-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-form__btn {
|
|
||||||
border: 0;
|
|
||||||
background: none;
|
|
||||||
padding: 0.25rem 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-form__label {
|
|
||||||
font-weight: 700;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-form__label-actions {
|
.chat-form__row {
|
||||||
margin-left: auto;
|
&.-separator {
|
||||||
|
border-bottom: 1px solid var(--primary-low);
|
||||||
.btn-text {
|
|
||||||
color: var(--tertiary);
|
|
||||||
font-size: var(--font-down-1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-retention-info {
|
.chat-form__section {
|
||||||
margin-top: 2rem;
|
display: flex;
|
||||||
color: var(--primary-high);
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
& + .chat-form__section {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: var(--font-down-1);
|
||||||
|
color: var(--primary-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title + &-content {
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
background: var(--primary-very-low);
|
||||||
|
gap: 1rem;
|
||||||
|
display: flex;
|
||||||
|
padding: 1rem;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-form__row {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
// background: green;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
label,
|
||||||
|
.d-toggle-switch__checkbox-slider {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-action {
|
||||||
|
.chat-form__btn:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-label + &-action {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.-link {
|
||||||
|
color: var(--primary);
|
||||||
|
|
||||||
.d-icon {
|
.d-icon {
|
||||||
margin-right: 0.5em;
|
color: var(--primary-medium);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 40px;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-description {
|
||||||
|
display: flex;
|
||||||
|
padding-top: 3px;
|
||||||
|
color: var(--primary-medium);
|
||||||
|
font-size: var(--font-down-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
@import "chat-channel-card";
|
@import "chat-channel-card";
|
||||||
@import "chat-channel-info";
|
@import "chat-channel-info";
|
||||||
@import "chat-channel-preview-card";
|
@import "chat-channel-preview-card";
|
||||||
@import "chat-channel-settings-saved-indicator";
|
|
||||||
@import "chat-channel-title";
|
@import "chat-channel-title";
|
||||||
@import "chat-composer-dropdown";
|
@import "chat-composer-dropdown";
|
||||||
@import "chat-composer-upload";
|
@import "chat-composer-upload";
|
||||||
@ -64,3 +63,5 @@
|
|||||||
@import "chat-modal-move-message-to-channel";
|
@import "chat-modal-move-message-to-channel";
|
||||||
@import "chat-scroll-to-bottom";
|
@import "chat-scroll-to-bottom";
|
||||||
@import "chat-channel-row";
|
@import "chat-channel-row";
|
||||||
|
@import "chat-channel-members";
|
||||||
|
@import "chat-channel-settings";
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
.chat-channel-members {
|
||||||
|
width: 100%;
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
.chat-channel-settings {
|
||||||
|
width: 100%;
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
@import "base-mobile";
|
@import "base-mobile";
|
||||||
@import "chat-channel-info";
|
|
||||||
@import "chat-channel";
|
@import "chat-channel";
|
||||||
@import "chat-composer";
|
@import "chat-composer";
|
||||||
@import "chat-index";
|
@import "chat-index";
|
||||||
@ -15,3 +14,5 @@
|
|||||||
@import "chat-message-thread-indicator";
|
@import "chat-message-thread-indicator";
|
||||||
@import "chat-message-creator";
|
@import "chat-message-creator";
|
||||||
@import "chat-channel-row";
|
@import "chat-channel-row";
|
||||||
|
@import "chat-channel-members";
|
||||||
|
@import "chat-channel-settings";
|
||||||
|
@ -320,7 +320,6 @@ en:
|
|||||||
back_to_all_channels: "All channels"
|
back_to_all_channels: "All channels"
|
||||||
back_to_channel: "Back"
|
back_to_channel: "Back"
|
||||||
tabs:
|
tabs:
|
||||||
about: About
|
|
||||||
members: Members
|
members: Members
|
||||||
settings: Settings
|
settings: Settings
|
||||||
|
|
||||||
@ -462,6 +461,11 @@ en:
|
|||||||
saved: "Saved"
|
saved: "Saved"
|
||||||
unfollow: "Leave"
|
unfollow: "Leave"
|
||||||
admin_title: "Admin"
|
admin_title: "Admin"
|
||||||
|
settings_title: "Settings"
|
||||||
|
info_title: "Channel info"
|
||||||
|
category_label: "Category"
|
||||||
|
history_label: "History"
|
||||||
|
members_label: "Members"
|
||||||
|
|
||||||
admin:
|
admin:
|
||||||
title: "Chat"
|
title: "Chat"
|
||||||
@ -538,14 +542,14 @@ en:
|
|||||||
other: "%{commaSeparatedUsernames} and %{count} others are typing"
|
other: "%{commaSeparatedUsernames} and %{count} others are typing"
|
||||||
|
|
||||||
retention_reminders:
|
retention_reminders:
|
||||||
public_none: "Channel history is retained indefinitely."
|
public_none: "indefinitely"
|
||||||
public:
|
public:
|
||||||
one: "Channel history is retained for %{count} day."
|
one: "%{count} day"
|
||||||
other: "Channel history is retained for %{count} days."
|
other: "%{count} days"
|
||||||
dm_none: "Personal chat history is retained indefinitely."
|
dm_none: "indefinitely"
|
||||||
dm:
|
dm:
|
||||||
one: "Personal chat history is retained for %{count} day."
|
one: "%{count} day"
|
||||||
other: "Personal chat history is retained for %{count} days."
|
other: "%{count} days"
|
||||||
|
|
||||||
flags:
|
flags:
|
||||||
off_topic: "This message is not relevant to the current discussion as defined by the channel title, and should probably be moved elsewhere."
|
off_topic: "This message is not relevant to the current discussion as defined by the channel title, and should probably be moved elsewhere."
|
||||||
|
@ -1,138 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
RSpec.describe "Channel - Info - About page", type: :system do
|
|
||||||
fab!(:channel_1) { Fabricate(:category_channel) }
|
|
||||||
|
|
||||||
let(:chat_page) { PageObjects::Pages::Chat.new }
|
|
||||||
let(:chat_channel_about_page) { PageObjects::Pages::ChatChannelAbout.new }
|
|
||||||
|
|
||||||
before { chat_system_bootstrap }
|
|
||||||
|
|
||||||
context "as regular user" do
|
|
||||||
fab!(:current_user) { Fabricate(:user) }
|
|
||||||
|
|
||||||
before { sign_in(current_user) }
|
|
||||||
|
|
||||||
it "shows channel info" do
|
|
||||||
chat_page.visit_channel_about(channel_1)
|
|
||||||
|
|
||||||
expect(page.find(".category-name")).to have_content(channel_1.chatable.name)
|
|
||||||
expect(page.find(".channel-info-about-view__name")).to have_content(channel_1.title)
|
|
||||||
expect(page.find(".channel-info-about-view__slug")).to have_content(channel_1.slug)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "escapes channel title" do
|
|
||||||
channel_1.update!(name: "<script>alert('hello')</script>")
|
|
||||||
chat_page.visit_channel_about(channel_1)
|
|
||||||
|
|
||||||
expect(page.find(".channel-info-about-view__name")["innerHTML"].strip).to eq(
|
|
||||||
"<script>alert('hello')</script>",
|
|
||||||
)
|
|
||||||
expect(page.find(".chat-channel-title__name")["innerHTML"].strip).to eq(
|
|
||||||
"<script>alert('hello')</script>",
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "can’t edit name or slug" do
|
|
||||||
chat_page.visit_channel_about(channel_1)
|
|
||||||
|
|
||||||
expect(page).to have_no_selector(".edit-name-slug-btn")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "can’t edit description" do
|
|
||||||
chat_page.visit_channel_about(channel_1)
|
|
||||||
|
|
||||||
expect(page).to have_no_selector(".edit-description-btn")
|
|
||||||
end
|
|
||||||
|
|
||||||
context "as a member" do
|
|
||||||
before { channel_1.add(current_user) }
|
|
||||||
|
|
||||||
it "can leave channel" do
|
|
||||||
chat_page.visit_channel_about(channel_1)
|
|
||||||
membership = channel_1.membership_for(current_user)
|
|
||||||
|
|
||||||
expect {
|
|
||||||
click_button(I18n.t("js.chat.channel_settings.leave_channel"))
|
|
||||||
expect(page).to have_content(I18n.t("js.chat.channel_settings.join_channel"))
|
|
||||||
}.to change { membership.reload.following }.from(true).to(false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "as not a member" do
|
|
||||||
it "can join channel" do
|
|
||||||
chat_page.visit_channel_about(channel_1)
|
|
||||||
|
|
||||||
expect {
|
|
||||||
click_button(I18n.t("js.chat.channel_settings.join_channel"))
|
|
||||||
expect(page).to have_content(I18n.t("js.chat.channel_settings.leave_channel"))
|
|
||||||
}.to change {
|
|
||||||
Chat::UserChatChannelMembership.where(user_id: current_user.id, following: true).count
|
|
||||||
}.by(1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "as admin" do
|
|
||||||
fab!(:current_user) { Fabricate(:admin) }
|
|
||||||
|
|
||||||
before { sign_in(current_user) }
|
|
||||||
|
|
||||||
it "can edit name" do
|
|
||||||
chat_page.visit_channel_about(channel_1)
|
|
||||||
|
|
||||||
edit_modal = chat_channel_about_page.open_edit_modal
|
|
||||||
|
|
||||||
expect(edit_modal).to have_name_input(channel_1.title)
|
|
||||||
|
|
||||||
name = "A new name"
|
|
||||||
|
|
||||||
edit_modal.fill_and_save_name(name)
|
|
||||||
|
|
||||||
expect(chat_channel_about_page).to have_name(name)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "can edit description" do
|
|
||||||
chat_page.visit_channel_about(channel_1)
|
|
||||||
find(".edit-description-btn").click
|
|
||||||
|
|
||||||
expect(page).to have_selector(
|
|
||||||
".chat-modal-edit-channel-description__description-input",
|
|
||||||
text: channel_1.description,
|
|
||||||
)
|
|
||||||
|
|
||||||
description = "A new description"
|
|
||||||
find(".chat-modal-edit-channel-description__description-input").fill_in(with: description)
|
|
||||||
find(".create").click
|
|
||||||
|
|
||||||
expect(page).to have_content(description)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "can edit slug" do
|
|
||||||
chat_page.visit_channel_about(channel_1)
|
|
||||||
edit_modal = chat_channel_about_page.open_edit_modal
|
|
||||||
|
|
||||||
slug = "gonzo-slug"
|
|
||||||
|
|
||||||
expect(edit_modal).to have_slug_input(channel_1.slug)
|
|
||||||
|
|
||||||
edit_modal.fill_and_save_slug(slug)
|
|
||||||
|
|
||||||
expect(chat_channel_about_page).to have_slug(slug)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "can clear the slug to use the autogenerated version based on the name" do
|
|
||||||
channel_1.update!(name: "test channel")
|
|
||||||
chat_page.visit_channel_about(channel_1)
|
|
||||||
edit_modal = chat_channel_about_page.open_edit_modal
|
|
||||||
|
|
||||||
expect(edit_modal).to have_slug_input(channel_1.slug)
|
|
||||||
|
|
||||||
edit_modal.fill_in_slug_input("")
|
|
||||||
edit_modal.wait_for_auto_generated_slug
|
|
||||||
edit_modal.save_changes
|
|
||||||
|
|
||||||
expect(chat_channel_about_page).to have_slug("test-channel")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,38 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
RSpec.describe "Info pages", type: :system do
|
|
||||||
let(:chat_page) { PageObjects::Pages::Chat.new }
|
|
||||||
let(:channel) { PageObjects::Pages::ChatChannel.new }
|
|
||||||
fab!(:current_user) { Fabricate(:user) }
|
|
||||||
fab!(:channel_1) { Fabricate(:chat_channel) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
chat_system_bootstrap
|
|
||||||
channel_1.add(current_user)
|
|
||||||
sign_in(current_user)
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when visiting from browse page" do
|
|
||||||
context "when clicking back button" do
|
|
||||||
it "redirects to browse page" do
|
|
||||||
chat_page.visit_browse
|
|
||||||
find(".chat-channel-card__setting").click
|
|
||||||
find(".chat-full-page-header__back-btn").click
|
|
||||||
|
|
||||||
expect(page).to have_current_path("/chat/browse/open")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when visiting from channel page" do
|
|
||||||
context "when clicking back button" do
|
|
||||||
it "redirects to channel page" do
|
|
||||||
chat_page.visit_channel(channel_1)
|
|
||||||
find(".chat-channel-title-wrapper").click
|
|
||||||
find(".chat-full-page-header__back-btn").click
|
|
||||||
|
|
||||||
expect(page).to have_current_path(chat.channel_path(channel_1.slug, channel_1.id))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -14,7 +14,7 @@ RSpec.describe "Channel - Info - Members page", type: :system do
|
|||||||
context "as unauthorized user" do
|
context "as unauthorized user" do
|
||||||
before { SiteSetting.chat_allowed_groups = Fabricate(:group).id }
|
before { SiteSetting.chat_allowed_groups = Fabricate(:group).id }
|
||||||
|
|
||||||
it "can’t see channel members" do
|
it "can't see channel members" do
|
||||||
chat_page.visit_channel_members(channel_1)
|
chat_page.visit_channel_members(channel_1)
|
||||||
|
|
||||||
expect(page).to have_current_path("/latest")
|
expect(page).to have_current_path("/latest")
|
||||||
@ -23,10 +23,10 @@ RSpec.describe "Channel - Info - Members page", type: :system do
|
|||||||
|
|
||||||
context "as authorized user" do
|
context "as authorized user" do
|
||||||
context "with no members" do
|
context "with no members" do
|
||||||
it "redirects to about page" do
|
it "redirects to settings page" do
|
||||||
chat_page.visit_channel_members(channel_1)
|
chat_page.visit_channel_members(channel_1)
|
||||||
|
|
||||||
expect(page).to have_current_path("/chat/c/#{channel_1.slug}/#{channel_1.id}/info/about")
|
expect(page).to have_current_path("/chat/c/#{channel_1.slug}/#{channel_1.id}/info/settings")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -44,15 +44,15 @@ RSpec.describe "Channel - Info - Members page", type: :system do
|
|||||||
|
|
||||||
chat_page.visit_channel_members(channel_1)
|
chat_page.visit_channel_members(channel_1)
|
||||||
|
|
||||||
expect(page).to have_selector(".channel-members-view__list-item", count: 50, wait: 15)
|
expect(page).to have_selector(".chat-channel-members__list-item", count: 60)
|
||||||
|
|
||||||
scroll_to(find(".channel-members-view__list-item:nth-child(50)"))
|
scroll_to(find(".chat-channel-members__list-item:nth-child(50)"))
|
||||||
|
|
||||||
expect(page).to have_selector(".channel-members-view__list-item", count: 100, wait: 15)
|
expect(page).to have_selector(".chat-channel-members__list-item", count: 100)
|
||||||
|
|
||||||
scroll_to(find(".channel-members-view__list-item:nth-child(100)"))
|
scroll_to(find(".chat-channel-members__list-item:nth-child(100)"))
|
||||||
|
|
||||||
expect(page).to have_selector(".channel-members-view__list-item", count: 100, wait: 15)
|
expect(page).to have_selector(".chat-channel-members__list-item", count: 100)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with filter" do
|
context "with filter" do
|
||||||
@ -62,9 +62,9 @@ RSpec.describe "Channel - Info - Members page", type: :system do
|
|||||||
Jobs::Chat::UpdateChannelUserCount.new.execute(chat_channel_id: channel_1.id)
|
Jobs::Chat::UpdateChannelUserCount.new.execute(chat_channel_id: channel_1.id)
|
||||||
|
|
||||||
chat_page.visit_channel_members(channel_1)
|
chat_page.visit_channel_members(channel_1)
|
||||||
find(".channel-members-view__search-input").fill_in(with: "cat")
|
find(".chat-channel-members__filter").fill_in(with: "cat")
|
||||||
|
|
||||||
expect(page).to have_selector(".channel-members-view__list-item", count: 1, text: "cat")
|
expect(page).to have_selector(".chat-channel-members__list-item", count: 1, text: "cat")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,15 +1,42 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe "Channel - Info - Settings page", type: :system do
|
RSpec.describe "Channel - Info - Settings page", type: :system do
|
||||||
let(:chat_page) { PageObjects::Pages::Chat.new }
|
|
||||||
fab!(:current_user) { Fabricate(:user) }
|
fab!(:current_user) { Fabricate(:user) }
|
||||||
fab!(:channel_1) { Fabricate(:category_channel) }
|
fab!(:channel_1) { Fabricate(:category_channel) }
|
||||||
|
|
||||||
|
let(:chat_page) { PageObjects::Pages::Chat.new }
|
||||||
|
let(:toasts) { PageObjects::Components::Toasts.new }
|
||||||
|
let(:channel_settings_page) { PageObjects::Pages::ChatChannelSettings.new }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
chat_system_bootstrap
|
chat_system_bootstrap
|
||||||
sign_in(current_user)
|
sign_in(current_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "when visiting from browse page" do
|
||||||
|
context "when clicking back button" do
|
||||||
|
it "redirects to browse page" do
|
||||||
|
chat_page.visit_browse
|
||||||
|
find(".chat-channel-card__setting").click
|
||||||
|
find(".chat-full-page-header__back-btn").click
|
||||||
|
|
||||||
|
expect(page).to have_current_path("/chat/browse/open")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when visiting from channel page" do
|
||||||
|
context "when clicking back button" do
|
||||||
|
it "redirects to channel page" do
|
||||||
|
chat_page.visit_channel(channel_1)
|
||||||
|
find(".chat-channel-title-wrapper").click
|
||||||
|
find(".chat-full-page-header__back-btn").click
|
||||||
|
|
||||||
|
expect(page).to have_current_path(chat.channel_path(channel_1.slug, channel_1.id))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "as unauthorized user" do
|
context "as unauthorized user" do
|
||||||
before { SiteSetting.chat_allowed_groups = Fabricate(:group).id }
|
before { SiteSetting.chat_allowed_groups = Fabricate(:group).id }
|
||||||
|
|
||||||
@ -20,61 +47,69 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "as authorized user" do
|
context "as not allowed to see the channel" do
|
||||||
context "as not member" do
|
fab!(:channel_1) { Fabricate(:private_category_channel) }
|
||||||
it "redirects to about tab" do
|
|
||||||
|
it "redirects to browse page" do
|
||||||
chat_page.visit_channel_settings(channel_1)
|
chat_page.visit_channel_settings(channel_1)
|
||||||
|
|
||||||
expect(page).to have_current_path("/chat/c/#{channel_1.slug}/#{channel_1.id}/info/about")
|
expect(page).to have_current_path("/chat/browse/open")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn’t have settings tab" do
|
context "as not member of channel" do
|
||||||
|
it "shows settings page" do
|
||||||
chat_page.visit_channel_settings(channel_1)
|
chat_page.visit_channel_settings(channel_1)
|
||||||
|
|
||||||
expect(page).to have_no_selector(".chat-tabs-list__item[aria-controls='settings-tab']")
|
expect(page).to have_current_path("/chat/c/#{channel_1.slug}/#{channel_1.id}/info/settings")
|
||||||
end
|
|
||||||
|
|
||||||
context "as an admin" do
|
|
||||||
before { sign_in(Fabricate(:admin)) }
|
|
||||||
|
|
||||||
it "shows settings tab" do
|
|
||||||
chat_page.visit_channel_settings(channel_1)
|
|
||||||
|
|
||||||
expect(page).to have_selector(".chat-tabs-list__item[aria-controls='settings-tab']")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "can navigate to settings tab" do
|
|
||||||
chat_page.visit_channel_settings(channel_1)
|
|
||||||
|
|
||||||
expect(page).to have_current_path(
|
|
||||||
"/chat/c/#{channel_1.slug}/#{channel_1.id}/info/settings",
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "as a member" do
|
context "as regular user of channel" do
|
||||||
before { channel_1.add(current_user) }
|
before { channel_1.add(current_user) }
|
||||||
|
|
||||||
context "when visiting the settings of a recently joined channel" do
|
it "shows settings page" do
|
||||||
fab!(:channel_2) { Fabricate(:category_channel) }
|
chat_page.visit_channel_settings(channel_1)
|
||||||
|
|
||||||
it "is correctly populated" do
|
expect(page).to have_current_path("/chat/c/#{channel_1.slug}/#{channel_1.id}/info/settings")
|
||||||
chat_page.visit_browse
|
|
||||||
find(
|
|
||||||
".chat-channel-card[data-channel-id='#{channel_2.id}'] .toggle-channel-membership-button",
|
|
||||||
).click
|
|
||||||
|
|
||||||
expect(
|
|
||||||
page.find(".chat-channel-card[data-channel-id='#{channel_2.id}']"),
|
|
||||||
).to have_content(I18n.t("js.chat.joined").upcase)
|
|
||||||
|
|
||||||
find(
|
|
||||||
".chat-channel-card[data-channel-id='#{channel_2.id}'] .chat-channel-card__setting",
|
|
||||||
).click
|
|
||||||
|
|
||||||
expect(page).to have_content(I18n.t("js.chat.notification_levels.mention"))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "shows channel info" do
|
||||||
|
chat_page.visit_channel_settings(channel_1)
|
||||||
|
|
||||||
|
expect(page.find(".category-name")).to have_content(channel_1.chatable.name)
|
||||||
|
expect(page.find(".chat-channel-settings__name")).to have_content(channel_1.title)
|
||||||
|
expect(page.find(".chat-channel-settings__slug")).to have_content(channel_1.slug)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can’t edit name or slug" do
|
||||||
|
chat_page.visit_channel_settings(channel_1)
|
||||||
|
|
||||||
|
expect(page).to have_no_selector(".edit-name-slug-btn")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can’t edit description" do
|
||||||
|
chat_page.visit_channel_settings(channel_1)
|
||||||
|
|
||||||
|
expect(page).to have_no_selector(".edit-description-btn")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "escapes channel title" do
|
||||||
|
channel_1.update!(name: "<script>alert('hello')</script>")
|
||||||
|
chat_page.visit_channel_settings(channel_1)
|
||||||
|
|
||||||
|
expect(page.find(".chat-channel-settings__name")["innerHTML"].strip).to eq(
|
||||||
|
"<script>alert('hello')</script>",
|
||||||
|
)
|
||||||
|
expect(page.find(".chat-channel-title__name")["innerHTML"].strip).to eq(
|
||||||
|
"<script>alert('hello')</script>",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not showing admin section" do
|
||||||
|
chat_page.visit_channel_settings(channel_1)
|
||||||
|
|
||||||
|
expect(page).to have_no_css("[data-section='admin']")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "can mute channel" do
|
it "can mute channel" do
|
||||||
@ -82,12 +117,9 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
|
|||||||
membership = channel_1.membership_for(current_user)
|
membership = channel_1.membership_for(current_user)
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
select_kit =
|
PageObjects::Components::DToggleSwitch.new(".chat-channel-settings__mute-switch").toggle
|
||||||
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"))
|
expect(toasts).to have_success(I18n.t("js.saved"))
|
||||||
}.to change { membership.reload.muted }.from(false).to(true)
|
}.to change { membership.reload.muted }.from(false).to(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -98,12 +130,12 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
|
|||||||
expect {
|
expect {
|
||||||
select_kit =
|
select_kit =
|
||||||
PageObjects::Components::SelectKit.new(
|
PageObjects::Components::SelectKit.new(
|
||||||
".-desktop-notification-level .channel-settings-view__selector",
|
".chat-channel-settings__desktop-notifications-selector",
|
||||||
)
|
)
|
||||||
select_kit.expand
|
select_kit.expand
|
||||||
select_kit.select_row_by_name("Never")
|
select_kit.select_row_by_name("Never")
|
||||||
|
|
||||||
expect(page).to have_content(I18n.t("js.chat.settings.saved"))
|
expect(toasts).to have_success(I18n.t("js.saved"))
|
||||||
}.to change { membership.reload.desktop_notification_level }.from("mention").to("never")
|
}.to change { membership.reload.desktop_notification_level }.from("mention").to("never")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -114,41 +146,92 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
|
|||||||
expect {
|
expect {
|
||||||
select_kit =
|
select_kit =
|
||||||
PageObjects::Components::SelectKit.new(
|
PageObjects::Components::SelectKit.new(
|
||||||
".-mobile-notification-level .channel-settings-view__selector",
|
".chat-channel-settings__mobile-notifications-selector",
|
||||||
)
|
)
|
||||||
select_kit.expand
|
select_kit.expand
|
||||||
select_kit.select_row_by_name("Never")
|
select_kit.select_row_by_name("Never")
|
||||||
|
|
||||||
expect(page).to have_content(I18n.t("js.chat.settings.saved"))
|
expect(toasts).to have_success(I18n.t("js.saved"))
|
||||||
}.to change { membership.reload.mobile_notification_level }.from("mention").to("never")
|
}.to change { membership.reload.mobile_notification_level }.from("mention").to("never")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn’t show admin section" do
|
|
||||||
chat_page.visit_channel_settings(channel_1)
|
|
||||||
|
|
||||||
expect(page).to have_no_content(I18n.t("js.chat.settings.admin_title"))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context "as an admin" do
|
context "as staff" do
|
||||||
before { sign_in(Fabricate(:admin)) }
|
fab!(:current_user) { Fabricate(:admin) }
|
||||||
|
|
||||||
it "shows admin section" do
|
it "can edit name" do
|
||||||
chat_page.visit_channel_settings(channel_1)
|
chat_page.visit_channel_settings(channel_1)
|
||||||
|
|
||||||
expect(page).to have_content(I18n.t("js.chat.settings.admin_title"))
|
edit_modal = channel_settings_page.open_edit_modal
|
||||||
|
|
||||||
|
expect(edit_modal).to have_name_input(channel_1.title)
|
||||||
|
|
||||||
|
name = "A new name"
|
||||||
|
|
||||||
|
edit_modal.fill_and_save_name(name)
|
||||||
|
|
||||||
|
expect(channel_settings_page).to have_name(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can edit description" do
|
||||||
|
chat_page.visit_channel_settings(channel_1)
|
||||||
|
find(".edit-description-btn").click
|
||||||
|
|
||||||
|
expect(page).to have_selector(
|
||||||
|
".chat-modal-edit-channel-description__description-input",
|
||||||
|
text: channel_1.description,
|
||||||
|
)
|
||||||
|
|
||||||
|
description = "A new description"
|
||||||
|
find(".chat-modal-edit-channel-description__description-input").fill_in(with: description)
|
||||||
|
find(".create").click
|
||||||
|
|
||||||
|
expect(page).to have_content(description)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can edit slug" do
|
||||||
|
chat_page.visit_channel_settings(channel_1)
|
||||||
|
edit_modal = channel_settings_page.open_edit_modal
|
||||||
|
|
||||||
|
slug = "gonzo-slug"
|
||||||
|
|
||||||
|
expect(edit_modal).to have_slug_input(channel_1.slug)
|
||||||
|
|
||||||
|
edit_modal.fill_and_save_slug(slug)
|
||||||
|
|
||||||
|
expect(channel_settings_page).to have_slug(slug)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can clear the slug to use the autogenerated version based on the name" do
|
||||||
|
channel_1.update!(name: "test channel")
|
||||||
|
chat_page.visit_channel_settings(channel_1)
|
||||||
|
edit_modal = channel_settings_page.open_edit_modal
|
||||||
|
|
||||||
|
expect(edit_modal).to have_slug_input(channel_1.slug)
|
||||||
|
|
||||||
|
edit_modal.fill_in_slug_input("")
|
||||||
|
edit_modal.wait_for_auto_generated_slug
|
||||||
|
edit_modal.save_changes
|
||||||
|
|
||||||
|
expect(channel_settings_page).to have_slug("test-channel")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "shows settings page" do
|
||||||
|
chat_page.visit_channel_settings(channel_1)
|
||||||
|
|
||||||
|
expect(page).to have_current_path("/chat/c/#{channel_1.slug}/#{channel_1.id}/info/settings")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "can change auto join setting" do
|
it "can change auto join setting" do
|
||||||
chat_page.visit_channel_settings(channel_1)
|
chat_page.visit_channel_settings(channel_1)
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
select_kit =
|
PageObjects::Components::DToggleSwitch.new(
|
||||||
PageObjects::Components::SelectKit.new(".-autojoin .channel-settings-view__selector")
|
".chat-channel-settings__auto-join-switch",
|
||||||
select_kit.expand
|
).toggle
|
||||||
select_kit.select_row_by_name("Yes")
|
|
||||||
find("#dialog-holder .btn-primary").click
|
find("#dialog-holder .btn-primary").click
|
||||||
|
|
||||||
expect(page).to have_content(I18n.t("js.chat.settings.saved"))
|
expect(toasts).to have_success(I18n.t("js.saved"))
|
||||||
}.to change { channel_1.reload.auto_join_users }.from(false).to(true)
|
}.to change { channel_1.reload.auto_join_users }.from(false).to(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -156,14 +239,11 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
|
|||||||
chat_page.visit_channel_settings(channel_1)
|
chat_page.visit_channel_settings(channel_1)
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
select_kit =
|
PageObjects::Components::DToggleSwitch.new(
|
||||||
PageObjects::Components::SelectKit.new(
|
".chat-channel-settings__channel-wide-mentions",
|
||||||
".-channel-wide-mentions .channel-settings-view__selector",
|
).toggle
|
||||||
)
|
|
||||||
select_kit.expand
|
|
||||||
select_kit.select_row_by_name("No")
|
|
||||||
|
|
||||||
expect(page).to have_content(I18n.t("js.chat.settings.saved"))
|
expect(toasts).to have_success(I18n.t("js.saved"))
|
||||||
}.to change { channel_1.reload.allow_channel_wide_mentions }.from(true).to(false)
|
}.to change { channel_1.reload.allow_channel_wide_mentions }.from(true).to(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -173,6 +253,7 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
|
|||||||
expect {
|
expect {
|
||||||
click_button(I18n.t("js.chat.channel_settings.close_channel"))
|
click_button(I18n.t("js.chat.channel_settings.close_channel"))
|
||||||
find("#chat-channel-toggle-btn").click
|
find("#chat-channel-toggle-btn").click
|
||||||
|
|
||||||
expect(page).to have_content(I18n.t("js.chat.channel_status.closed_header"))
|
expect(page).to have_content(I18n.t("js.chat.channel_status.closed_header"))
|
||||||
}.to change { channel_1.reload.status }.from("open").to("closed")
|
}.to change { channel_1.reload.status }.from("open").to("closed")
|
||||||
end
|
end
|
||||||
@ -181,11 +262,11 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
|
|||||||
chat_page.visit_channel_settings(channel_1)
|
chat_page.visit_channel_settings(channel_1)
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
select_kit =
|
PageObjects::Components::DToggleSwitch.new(
|
||||||
PageObjects::Components::SelectKit.new(".-threading .channel-settings-view__selector")
|
".chat-channel-settings__threading-switch",
|
||||||
select_kit.expand
|
).toggle
|
||||||
select_kit.select_row_by_name("Enabled")
|
|
||||||
expect(page).to have_content(I18n.t("js.chat.settings.saved"))
|
expect(toasts).to have_success(I18n.t("js.saved"))
|
||||||
}.to change { channel_1.reload.threading_enabled }.from(false).to(true)
|
}.to change { channel_1.reload.threading_enabled }.from(false).to(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -198,8 +279,7 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
|
|||||||
expect(page).to have_content(I18n.t("js.chat.channel_delete.process_started"))
|
expect(page).to have_content(I18n.t("js.chat.channel_delete.process_started"))
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when confirmation name is wrong" do
|
it "doesn’t delete when confirmation is wrong" do
|
||||||
it "doesn’t delete submission" do
|
|
||||||
chat_page.visit_channel_settings(channel_1)
|
chat_page.visit_channel_settings(channel_1)
|
||||||
find(".delete-btn").click
|
find(".delete-btn").click
|
||||||
fill_in("channel-delete-confirm-name", with: channel_1.title + "wrong")
|
fill_in("channel-delete-confirm-name", with: channel_1.title + "wrong")
|
||||||
@ -207,7 +287,4 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
|
|||||||
expect(page).to have_button("chat-confirm-delete-channel", disabled: true)
|
expect(page).to have_button("chat-confirm-delete-channel", disabled: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -24,7 +24,7 @@ RSpec.describe "Drawer", type: :system do
|
|||||||
drawer_page.open_channel(channel)
|
drawer_page.open_channel(channel)
|
||||||
page.find(".chat-channel-title").click
|
page.find(".chat-channel-title").click
|
||||||
|
|
||||||
expect(page).to have_current_path("/chat/c/#{channel.slug}/#{channel.id}/info/about")
|
expect(page).to have_current_path("/chat/c/#{channel.slug}/#{channel.id}/info/settings")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -63,10 +63,6 @@ module PageObjects
|
|||||||
visit(channel.url + "/info/settings")
|
visit(channel.url + "/info/settings")
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit_channel_about(channel)
|
|
||||||
visit(channel.url + "/info/about")
|
|
||||||
end
|
|
||||||
|
|
||||||
def visit_channel_members(channel)
|
def visit_channel_members(channel)
|
||||||
visit(channel.url + "/info/members")
|
visit(channel.url + "/info/members")
|
||||||
end
|
end
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
module PageObjects
|
module PageObjects
|
||||||
module Pages
|
module Pages
|
||||||
class ChatChannelAbout < PageObjects::Pages::Base
|
class ChatChannelSettings < PageObjects::Pages::Base
|
||||||
EDIT_MODAL_SELECTOR = ".chat-modal-edit-channel-name"
|
EDIT_MODAL_SELECTOR = ".chat-modal-edit-channel-name"
|
||||||
|
|
||||||
def open_edit_modal
|
def open_edit_modal
|
||||||
@ -12,11 +12,11 @@ module PageObjects
|
|||||||
end
|
end
|
||||||
|
|
||||||
def has_slug?(slug)
|
def has_slug?(slug)
|
||||||
page.has_css?(".channel-info-about-view__slug", text: slug)
|
page.has_css?(".chat-channel-settings__slug", text: slug)
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_name?(name)
|
def has_name?(name)
|
||||||
page.has_css?(".channel-info-about-view__name", text: name)
|
page.has_css?(".chat-channel-settings__name", text: name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -5,7 +5,7 @@ module PageObjects
|
|||||||
class ChatChannelEdit < PageObjects::Modals::Base
|
class ChatChannelEdit < PageObjects::Modals::Base
|
||||||
include SystemHelpers
|
include SystemHelpers
|
||||||
|
|
||||||
EDIT_MODAL_SELECTOR = PageObjects::Pages::ChatChannelAbout::EDIT_MODAL_SELECTOR
|
EDIT_MODAL_SELECTOR = PageObjects::Pages::ChatChannelSettings::EDIT_MODAL_SELECTOR
|
||||||
SLUG_INPUT_SELECTOR = ".chat-channel-edit-name-slug-modal__slug-input"
|
SLUG_INPUT_SELECTOR = ".chat-channel-edit-name-slug-modal__slug-input"
|
||||||
NAME_INPUT_SELECTOR = ".chat-channel-edit-name-slug-modal__name-input"
|
NAME_INPUT_SELECTOR = ".chat-channel-edit-name-slug-modal__name-input"
|
||||||
|
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
import { module, test } from "qunit";
|
|
||||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
|
||||||
import { render, settled } from "@ember/test-helpers";
|
|
||||||
import { hbs } from "ember-cli-htmlbars";
|
|
||||||
|
|
||||||
module(
|
|
||||||
"Discourse Chat | Component | chat-channel-settings-saved-indicator",
|
|
||||||
function (hooks) {
|
|
||||||
setupRenderingTest(hooks);
|
|
||||||
|
|
||||||
test("when property changes", async function (assert) {
|
|
||||||
await render(
|
|
||||||
hbs`<ChatChannelSettingsSavedIndicator @property={{this.property}} />`
|
|
||||||
);
|
|
||||||
|
|
||||||
assert
|
|
||||||
.dom(".chat-channel-settings-saved-indicator.is-active")
|
|
||||||
.doesNotExist();
|
|
||||||
|
|
||||||
this.set("property", 1);
|
|
||||||
|
|
||||||
assert.dom(".chat-channel-settings-saved-indicator.is-active").exists();
|
|
||||||
|
|
||||||
await settled();
|
|
||||||
|
|
||||||
assert
|
|
||||||
.dom(".chat-channel-settings-saved-indicator.is-active")
|
|
||||||
.doesNotExist();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
@ -1,25 +0,0 @@
|
|||||||
import ChatChannel from "discourse/plugins/chat/discourse/models/chat-channel";
|
|
||||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
|
||||||
import hbs from "htmlbars-inline-precompile";
|
|
||||||
import I18n from "I18n";
|
|
||||||
import { module, test } from "qunit";
|
|
||||||
import { render } from "@ember/test-helpers";
|
|
||||||
|
|
||||||
module(
|
|
||||||
"Discourse Chat | Component | chat-channel-settings-view",
|
|
||||||
function (hooks) {
|
|
||||||
setupRenderingTest(hooks);
|
|
||||||
|
|
||||||
test("display retention info", async function (assert) {
|
|
||||||
this.set("channel", ChatChannel.create({ chatable_type: "Category" }));
|
|
||||||
|
|
||||||
await render(hbs`<ChatChannelSettingsView @channel={{this.channel}} />`);
|
|
||||||
|
|
||||||
assert.dom(".chat-retention-info").hasText(
|
|
||||||
I18n.t("chat.retention_reminders.public", {
|
|
||||||
count: this.siteSettings.chat_channel_retention_days,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
22
spec/system/page_objects/components/d_toggle_switch.rb
Normal file
22
spec/system/page_objects/components/d_toggle_switch.rb
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module PageObjects
|
||||||
|
module Components
|
||||||
|
class DToggleSwitch < PageObjects::Components::Base
|
||||||
|
attr_reader :context
|
||||||
|
|
||||||
|
def initialize(context)
|
||||||
|
@context = context
|
||||||
|
end
|
||||||
|
|
||||||
|
def component
|
||||||
|
find(@context, visible: :all).native
|
||||||
|
end
|
||||||
|
|
||||||
|
def toggle
|
||||||
|
actionbuilder = page.driver.browser.action # workaround zero height button
|
||||||
|
actionbuilder.click(component).perform
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user