FEATURE: load chat channel settings within drawer (#27346)

This change allows chat drawer users to edit channel settings and members without leaving drawer mode. If a channel is open within chat drawer and the user clicks the Channel name, it will load channel settings within the drawer.
This commit is contained in:
David Battersby 2024-06-06 14:01:09 +04:00 committed by GitHub
parent 6e56a76b20
commit 891fb17f60
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 242 additions and 49 deletions

View File

@ -0,0 +1,60 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { service } from "@ember/service";
import I18n from "discourse-i18n";
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
import ChannelMembers from "discourse/plugins/chat/discourse/components/chat/routes/channel-info-members";
import ChannelInfoNav from "discourse/plugins/chat/discourse/components/chat/routes/channel-info-nav";
export default class ChatDrawerRoutesMembers extends Component {
@service chat;
@service chatStateManager;
@service chatChannelsManager;
get backButton() {
return {
route: "chat.channel",
models: this.chat.activeChannel?.routeModels,
title: I18n.t("chat.return_to_channel"),
};
}
@action
async fetchChannel() {
if (!this.args.params?.channelId) {
return;
}
const channel = await this.chatChannelsManager.find(
this.args.params.channelId
);
this.chat.activeChannel = channel;
}
<template>
<Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|>
<navbar.BackButton
@title={{this.backButton.title}}
@route={{this.backButton.route}}
@routeModels={{this.backButton.models}}
/>
<navbar.ChannelTitle @channel={{this.chat.activeChannel}} />
<navbar.Actions as |a|>
<a.ToggleDrawerButton />
<a.FullPageButton />
<a.CloseDrawerButton />
</navbar.Actions>
</Navbar>
{{#if this.chatStateManager.isDrawerExpanded}}
<div class="chat-drawer-content" {{didInsert this.fetchChannel}}>
{{#if this.chat.activeChannel}}
<ChannelInfoNav @channel={{this.chat.activeChannel}} @tab="members" />
<ChannelMembers @channel={{this.chat.activeChannel}} />
{{/if}}
</div>
{{/if}}
</template>
}

View File

@ -0,0 +1,63 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { service } from "@ember/service";
import I18n from "discourse-i18n";
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
import ChannelInfoNav from "discourse/plugins/chat/discourse/components/chat/routes/channel-info-nav";
import ChannelSettings from "discourse/plugins/chat/discourse/components/chat/routes/channel-info-settings";
export default class ChatDrawerRoutesSettings extends Component {
@service chat;
@service chatStateManager;
@service chatChannelsManager;
get backButton() {
return {
route: "chat.channel",
models: this.chat.activeChannel?.routeModels,
title: I18n.t("chat.return_to_channel"),
};
}
@action
async fetchChannel() {
if (!this.args.params?.channelId) {
return;
}
const channel = await this.chatChannelsManager.find(
this.args.params.channelId
);
this.chat.activeChannel = channel;
}
<template>
<Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|>
<navbar.BackButton
@title={{this.backButton.title}}
@route={{this.backButton.route}}
@routeModels={{this.backButton.models}}
/>
<navbar.ChannelTitle @channel={{this.chat.activeChannel}} />
<navbar.Actions as |a|>
<a.ToggleDrawerButton />
<a.FullPageButton />
<a.CloseDrawerButton />
</navbar.Actions>
</Navbar>
{{#if this.chatStateManager.isDrawerExpanded}}
<div class="chat-drawer-content" {{didInsert this.fetchChannel}}>
{{#if this.chat.activeChannel}}
<ChannelInfoNav
@channel={{this.chat.activeChannel}}
@tab="settings"
/>
<ChannelSettings @channel={{this.chat.activeChannel}} />
{{/if}}
</div>
{{/if}}
</template>
}

View File

@ -54,10 +54,7 @@ export default class ChatModalEditChannelName extends Component {
this.channel.title = result.channel.title;
this.channel.slug = result.channel.slug;
await this.args.closeModal();
await this.router.replaceWith(
"chat.channel",
...this.channel.routeModels
);
this.router.replaceWith("chat.channel", ...this.channel.routeModels);
} catch (error) {
this.flash = extractError(error);
}

View File

@ -0,0 +1,47 @@
import Component from "@glimmer/component";
import { LinkTo } from "@ember/routing";
import { service } from "@ember/service";
import { eq } from "truth-helpers";
import i18n from "discourse-common/helpers/i18n";
export default class ChatRoutesChannelInfoNav extends Component {
@service site;
get showTabs() {
return this.site.desktopView && this.args.channel.isOpen;
}
<template>
{{#if this.showTabs}}
<nav class="c-channel-info__nav">
<ul class="nav nav-pills">
<li>
<LinkTo
@route="chat.channel.info.settings"
@models={{@channel.routeModels}}
class={{if (eq @tab "settings") "active"}}
@replace={{true}}
>
{{i18n "chat.channel_info.tabs.settings"}}
</LinkTo>
</li>
<li>
<LinkTo
@route="chat.channel.info.members"
@models={{@channel.routeModels}}
class={{if (eq @tab "members") "active"}}
@replace={{true}}
>
{{i18n "chat.channel_info.tabs.members"}}
{{#if @channel.isCategoryChannel}}
<span
class="c-channel-info__member-count"
>({{@channel.membershipsCount}})</span>
{{/if}}
</LinkTo>
</li>
</ul>
</nav>
{{/if}}
</template>
}

View File

@ -1,11 +1,11 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { LinkTo } from "@ember/routing";
import { service } from "@ember/service";
import I18n from "discourse-i18n";
import i18n from "discourse-common/helpers/i18n";
import ChatModalEditChannelName from "discourse/plugins/chat/discourse/components/chat/modal/edit-channel-name";
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
import ChatChannelStatus from "discourse/plugins/chat/discourse/components/chat-channel-status";
import ChannelInfoNav from "./channel-info-nav";
export default class ChatRoutesChannelInfo extends Component {
@service chatChannelInfoRouteOriginManager;
@ -13,15 +13,6 @@ export default class ChatRoutesChannelInfo extends Component {
@service modal;
@service chatGuardian;
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_channels");
backToAllChannelsLabel = I18n.t("chat.channel_info.back_to_channel");
get showTabs() {
return this.site.desktopView && this.args.channel.isOpen;
}
get canEditChannel() {
return (
this.chatGuardian.canEditChatChannel() &&
@ -44,13 +35,13 @@ export default class ChatRoutesChannelInfo extends Component {
{{#if this.chatChannelInfoRouteOriginManager.isBrowse}}
<navbar.BackButton
@route="chat.browse"
@title={{this.backToAllChannelsLabel}}
@title={{i18n "chat.channel_info.back_to_all_channels"}}
/>
{{else}}
<navbar.BackButton
@route="chat.channel"
@routeModels={{@channel.routeModels}}
@title={{this.backToChannelLabel}}
@title={{i18n "chat.channel_info.back_to_channel"}}
/>
{{/if}}
<navbar.ChannelTitle @channel={{@channel}} />
@ -59,36 +50,7 @@ export default class ChatRoutesChannelInfo extends Component {
<ChatChannelStatus @channel={{@channel}} />
<div class="c-channel-info">
{{#if this.showTabs}}
<nav class="c-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}}
{{#if @channel.isCategoryChannel}}
<span
class="c-channel-info__member-count"
>({{@channel.membershipsCount}})</span>
{{/if}}
</LinkTo>
</li>
</ul>
</nav>
{{/if}}
<ChannelInfoNav @channel={{@channel}} />
{{outlet}}
</div>
</div>

View File

@ -39,6 +39,8 @@ export default class ChatRoute extends DiscourseRoute {
"chat.channel.index",
"chat.channel.near-message",
"chat.channel-legacy",
"chat.channel.info.settings",
"chat.channel.info.members",
"chat",
"chat.index",
];

View File

@ -97,6 +97,9 @@ export default class ChatChannelsManager extends Service {
}
remove(model) {
if (!model) {
return;
}
this.chatSubscriptionsManager.stopChannelSubscription(model);
delete this._cached[model.id];
}

View File

@ -4,6 +4,8 @@ import ChatDrawerRoutesChannel from "discourse/plugins/chat/discourse/components
import ChatDrawerRoutesChannelThread from "discourse/plugins/chat/discourse/components/chat/drawer-routes/channel-thread";
import ChatDrawerRoutesChannelThreads from "discourse/plugins/chat/discourse/components/chat/drawer-routes/channel-threads";
import ChatDrawerRoutesChannels from "discourse/plugins/chat/discourse/components/chat/drawer-routes/channels";
import ChatDrawerRoutesMembers from "discourse/plugins/chat/discourse/components/chat/drawer-routes/members";
import ChatDrawerRoutesSettings from "discourse/plugins/chat/discourse/components/chat/drawer-routes/settings";
import ChatDrawerRoutesThreads from "discourse/plugins/chat/discourse/components/chat/drawer-routes/threads";
const ROUTES = {
@ -66,6 +68,22 @@ const ROUTES = {
};
},
},
"chat.channel.info.settings": {
name: ChatDrawerRoutesSettings,
extractParams: (route) => {
return {
channelId: route.parent.params.channelId,
};
},
},
"chat.channel.info.members": {
name: ChatDrawerRoutesMembers,
extractParams: (route) => {
return {
channelId: route.parent.params.channelId,
};
},
},
"chat.channel-legacy": {
name: ChatDrawerRoutesChannel,
extractParams: (route) => {

View File

@ -43,6 +43,19 @@
}
}
.chat-drawer-container .c-channel-settings,
.chat-drawer-container .c-channel-info__nav,
.chat-drawer-container .c-channel-members,
.chat-drawer-container .chat-message-creator-container {
padding: 1rem;
width: auto;
}
.chat-drawer-container .c-channel-info__nav,
.chat-drawer-container .c-channel-info__nav .nav-pills {
padding-bottom: 0;
}
.chat-drawer-container .c-footer .chat-channel-unread-indicator.-urgent {
width: min-content;
}

View File

@ -9,6 +9,7 @@ RSpec.describe "Drawer", type: :system do
before do
chat_system_bootstrap
sign_in(current_user)
chat_page.prefers_drawer
end
context "when on channel" do
@ -18,13 +19,32 @@ RSpec.describe "Drawer", type: :system do
end
context "when clicking channel title" do
it "opens channel settings page" do
before do
visit("/")
chat_page.open_from_header
drawer_page.open_channel(channel)
page.find(".c-navbar__channel-title").click
end
expect(page).to have_current_path("/chat/c/#{channel.slug}/#{channel.id}/info/settings")
it "opens channel settings page" do
expect(drawer_page).to have_channel_settings
end
it "has tabs for settings and members" do
expect(drawer_page).to have_css(".c-channel-info__nav li a", text: "Settings")
expect(drawer_page).to have_css(".c-channel-info__nav li a", text: "Members")
end
it "opens correct tab when clicked" do
page.find(".c-channel-info__nav li a", text: "Members").click
expect(drawer_page).to have_channel_members
page.find(".c-channel-info__nav li a", text: "Settings").click
expect(drawer_page).to have_channel_settings
end
it "has a back button" do
expect(drawer_page).to have_css(".c-navbar__back-button")
end
end
end

View File

@ -88,6 +88,14 @@ module PageObjects
has_css?("#{VISIBLE_DRAWER} .chat-channel[data-id='#{channel.id}']")
end
def has_channel_settings?
has_css?("#{VISIBLE_DRAWER} .c-channel-settings")
end
def has_channel_members?
has_css?("#{VISIBLE_DRAWER} .c-channel-members")
end
def has_open_thread_list?
has_css?("#{VISIBLE_DRAWER} .chat-thread-list")
end