FEATURE: allows browse page in chat drawer (#27919)

This commit ensures the browse page can be loaded in the drawer and doesn’t force full page mode.

Other notable changes of this commit:
- be consistent about wrapping each full page route with "c-routes.--route-name" and each drawer container with "c-drawer-routes.--route-name"
- move browse channels into its own component, it was before in the template of the channels browse
This commit is contained in:
Joffrey JAFFEUX 2024-07-16 12:34:37 +02:00 committed by GitHub
parent 600f2854c7
commit c74fa300e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 882 additions and 700 deletions

View File

@ -0,0 +1,129 @@
import Component from "@glimmer/component";
import { cached, tracked } from "@glimmer/tracking";
import { concat, hash } from "@ember/helper";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { LinkTo } from "@ember/routing";
import { schedule } from "@ember/runloop";
import { service } from "@ember/service";
import { eq } from "truth-helpers";
import DButton from "discourse/components/d-button";
import { INPUT_DELAY } from "discourse-common/config/environment";
import i18n from "discourse-common/helpers/i18n";
import discourseDebounce from "discourse-common/lib/debounce";
import List from "discourse/plugins/chat/discourse/components/chat/list";
import ChatModalNewMessage from "discourse/plugins/chat/discourse/components/chat/modal/new-message";
import ChatChannelCard from "discourse/plugins/chat/discourse/components/chat-channel-card";
import DcFilterInput from "discourse/plugins/chat/discourse/components/dc-filter-input";
const ARCHIVED = "archived";
const ALL = "all";
const OPEN = "open";
const CLOSED = "closed";
const TABS = [ALL, OPEN, CLOSED, ARCHIVED];
export default class BrowseChannels extends Component {
@service chatApi;
@service modal;
@service siteSettings;
@tracked filter = "";
get currentTab() {
return this.args.currentTab ?? ALL;
}
@cached
get channelsCollection() {
return this.chatApi.channels({
filter: this.filter,
status: this.currentTab,
});
}
get tabs() {
if (this.siteSettings.chat_allow_archiving_channels) {
return TABS;
} else {
return [...TABS].removeObject(ARCHIVED);
}
}
@action
showChatNewMessageModal() {
this.modal.show(ChatModalNewMessage);
}
@action
setFilter(event) {
this.filter = event.target.value;
discourseDebounce(this.debouncedLoad, INPUT_DELAY);
}
@action
debouncedLoad() {
this.channelsCollection.load({ limit: 10 });
}
@action
focusFilterInput(input) {
schedule("afterRender", () => input?.focus());
}
<template>
<div class="chat-browse-view">
<div class="chat-browse-view__actions">
<nav>
<ul class="nav-pills chat-browse-view__filters">
{{#each this.tabs as |tab|}}
<li class={{concat "chat-browse-view__filter -" tab}}>
<LinkTo
@route={{concat "chat.browse." tab}}
class={{concat "chat-browse-view__filter-link -" tab}}
@current-when={{eq tab this.currentTab}}
>
{{i18n (concat "chat.browse.filter_" tab)}}
</LinkTo>
</li>
{{/each}}
</ul>
</nav>
<DcFilterInput
{{didInsert this.focusFilterInput}}
@filterAction={{this.setFilter}}
@icons={{hash right="search"}}
@containerClass="filter-input"
placeholder={{i18n "chat.browse.filter_input_placeholder"}}
/>
</div>
<div class="chat-browse-view__content_wrapper">
<div class="chat-browse-view__content">
<List
@collection={{this.channelsCollection}}
class="chat-browse-view__cards"
as |list|
>
<list.Item as |channel|>
<ChatChannelCard @channel={{channel}} />
</list.Item>
<list.EmptyState>
<span class="empty-state-title">
{{i18n "chat.empty_state.title"}}
</span>
<div class="empty-state-body">
<p>{{i18n "chat.empty_state.direct_message"}}</p>
<DButton
@action={{this.showChatNewMessageModal}}
@label="chat.empty_state.direct_message_cta"
/>
</div>
</list.EmptyState>
</List>
</div>
</div>
</div>
</template>
}

View File

@ -702,7 +702,6 @@ export default class ChatChannel extends Component {
{{didUpdate this.loadMessages @targetMessageId}} {{didUpdate this.loadMessages @targetMessageId}}
data-id={{@channel.id}} data-id={{@channel.id}}
> >
<ChatChannelStatus @channel={{@channel}} /> <ChatChannelStatus @channel={{@channel}} />
<ChatNotices @channel={{@channel}} /> <ChatNotices @channel={{@channel}} />
<ChatMentionWarnings /> <ChatMentionWarnings />

View File

@ -0,0 +1,47 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { array } from "@ember/helper";
import { service } from "@ember/service";
import i18n from "discourse-common/helpers/i18n";
import BrowseChannels from "discourse/plugins/chat/discourse/components/browse-channels";
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
export default class ChatDrawerRoutesBrowse extends Component {
@service chat;
@service chatStateManager;
@service chatChannelsManager;
@service chatHistory;
@tracked showThreadFullTitle = false;
get showfullTitle() {
return this.chatStateManager.isDrawerExpanded && this.showThreadFullTitle;
}
<template>
<div class="c-drawer-routes --browse">
<Navbar
@onClick={{this.chat.toggleDrawer}}
@showFullTitle={{this.showfullTitle}}
as |navbar|
>
<navbar.BackButton @route="chat.channels" />
<navbar.Title @title={{i18n "chat.browse.title"}} />
<navbar.Actions as |a|>
<a.NewChannelButton />
<a.ToggleDrawerButton />
<a.FullPageButton />
<a.CloseDrawerButton />
</navbar.Actions>
</Navbar>
{{#if this.chatStateManager.isDrawerExpanded}}
<div class="chat-drawer-content">
{{#each (array @params.currentTab) as |tab|}}
<BrowseChannels @currentTab={{tab}} />
{{/each}}
</div>
{{/if}}
</div>
</template>
}

View File

@ -34,27 +34,35 @@ export default class ChatDrawerRoutesMembers extends Component {
} }
<template> <template>
<Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|> <div
<navbar.BackButton class="c-drawer-routes --channel-info-members"
@title={{this.backButton.title}} {{didInsert this.fetchChannel}}
@route={{this.backButton.route}} >
@routeModels={{this.backButton.models}} {{#if this.chat.activeChannel}}
/> <Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|>
<navbar.ChannelTitle @channel={{this.chat.activeChannel}} /> <navbar.BackButton
<navbar.Actions as |a|> @title={{this.backButton.title}}
<a.ToggleDrawerButton /> @route={{this.backButton.route}}
<a.FullPageButton /> @routeModels={{this.backButton.models}}
<a.CloseDrawerButton /> />
</navbar.Actions> <navbar.ChannelTitle @channel={{this.chat.activeChannel}} />
</Navbar> <navbar.Actions as |a|>
<a.ToggleDrawerButton />
<a.FullPageButton />
<a.CloseDrawerButton />
</navbar.Actions>
</Navbar>
{{#if this.chatStateManager.isDrawerExpanded}} {{#if this.chatStateManager.isDrawerExpanded}}
<div class="chat-drawer-content" {{didInsert this.fetchChannel}}> <div class="chat-drawer-content">
{{#if this.chat.activeChannel}} <ChannelInfoNav
<ChannelInfoNav @channel={{this.chat.activeChannel}} @tab="members" /> @channel={{this.chat.activeChannel}}
<ChannelMembers @channel={{this.chat.activeChannel}} /> @tab="members"
/>
<ChannelMembers @channel={{this.chat.activeChannel}} />
</div>
{{/if}} {{/if}}
</div> {{/if}}
{{/if}} </div>
</template> </template>
} }

View File

@ -34,30 +34,35 @@ export default class ChatDrawerRoutesSettings extends Component {
} }
<template> <template>
<Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|> <div
<navbar.BackButton class="c-drawer-routes --channel-info-settings"
@title={{this.backButton.title}} {{didInsert this.fetchChannel}}
@route={{this.backButton.route}} >
@routeModels={{this.backButton.models}} {{#if this.chat.activeChannel}}
/> <Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|>
<navbar.ChannelTitle @channel={{this.chat.activeChannel}} /> <navbar.BackButton
<navbar.Actions as |a|> @title={{this.backButton.title}}
<a.ToggleDrawerButton /> @route={{this.backButton.route}}
<a.FullPageButton /> @routeModels={{this.backButton.models}}
<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}} /> <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">
<ChannelInfoNav
@channel={{this.chat.activeChannel}}
@tab="settings"
/>
<ChannelSettings @channel={{this.chat.activeChannel}} />
</div>
{{/if}} {{/if}}
</div> {{/if}}
{{/if}} </div>
</template> </template>
} }

View File

@ -78,41 +78,45 @@ export default class ChatDrawerRoutesChannelThread extends Component {
} }
<template> <template>
<Navbar <div
@onClick={{this.chat.toggleDrawer}} class="c-drawer-routes --channel-thread"
@showFullTitle={{this.showfullTitle}} {{didInsert this.fetchChannelAndThread}}
as |navbar| {{didUpdate this.fetchChannelAndThread @params.channelId}}
{{didUpdate this.fetchChannelAndThread @params.threadId}}
> >
<navbar.BackButton {{#if this.chat.activeChannel}}
@title={{this.backButton.title}} <Navbar
@route={{this.backButton.route}} @onClick={{this.chat.toggleDrawer}}
@routeModels={{this.backButton.models}} @showFullTitle={{this.showfullTitle}}
/> as |navbar|
<navbar.Title @title={{this.threadTitle}} @icon="discourse-threads" /> >
<navbar.Actions as |a|> <navbar.BackButton
<a.ToggleDrawerButton /> @title={{this.backButton.title}}
<a.FullPageButton /> @route={{this.backButton.route}}
<a.CloseDrawerButton /> @routeModels={{this.backButton.models}}
</navbar.Actions> />
</Navbar> <navbar.Title @title={{this.threadTitle}} @icon="discourse-threads" />
<navbar.Actions as |a|>
<a.ToggleDrawerButton />
<a.FullPageButton />
<a.CloseDrawerButton />
</navbar.Actions>
</Navbar>
{{#if this.chatStateManager.isDrawerExpanded}} {{#if this.chatStateManager.isDrawerExpanded}}
<div <div class="chat-drawer-content">
class="chat-drawer-content" {{#each (array this.chat.activeChannel.activeThread) as |thread|}}
{{didInsert this.fetchChannelAndThread}} {{#if thread}}
{{didUpdate this.fetchChannelAndThread @params.channelId}} <ChatThread
{{didUpdate this.fetchChannelAndThread @params.threadId}} @thread={{thread}}
> @targetMessageId={{@params.messageId}}
{{#each (array this.chat.activeChannel.activeThread) as |thread|}} @setFullTitle={{this.setFullTitle}}
{{#if thread}} />
<ChatThread {{/if}}
@thread={{thread}} {{/each}}
@targetMessageId={{@params.messageId}} </div>
@setFullTitle={{this.setFullTitle}} {{/if}}
/> {{/if}}
{{/if}} </div>
{{/each}}
</div>
{{/if}}
</template> </template>
} }

View File

@ -40,29 +40,31 @@ export default class ChatDrawerRoutesChannelThreads extends Component {
} }
<template> <template>
{{#if this.chat.activeChannel}} <div class="c-drawer-routes --channel-threads">
<Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|>
<navbar.BackButton
@title={{this.backLinkTitle}}
@route="chat.channel"
@routeModels={{this.chat.activeChannel.routeModels}}
/>
<navbar.Title @title={{this.title}} @icon="discourse-threads" />
<navbar.Actions as |a|>
<a.ToggleDrawerButton />
<a.FullPageButton />
<a.CloseDrawerButton />
</navbar.Actions>
</Navbar>
{{/if}}
<div class="chat-drawer-content" {{didInsert this.fetchChannel}}>
{{#if this.chat.activeChannel}} {{#if this.chat.activeChannel}}
<ChatThreadList <Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|>
@channel={{this.chat.activeChannel}} <navbar.BackButton
@includeHeader={{false}} @title={{this.backLinkTitle}}
/> @route="chat.channel"
@routeModels={{this.chat.activeChannel.routeModels}}
/>
<navbar.Title @title={{this.title}} @icon="discourse-threads" />
<navbar.Actions as |a|>
<a.ToggleDrawerButton />
<a.FullPageButton />
<a.CloseDrawerButton />
</navbar.Actions>
</Navbar>
{{/if}} {{/if}}
<div class="chat-drawer-content" {{didInsert this.fetchChannel}}>
{{#if this.chat.activeChannel}}
<ChatThreadList
@channel={{this.chat.activeChannel}}
@includeHeader={{false}}
/>
{{/if}}
</div>
</div> </div>
</template> </template>
} }

View File

@ -11,9 +11,12 @@ export default class ChatDrawerRoutesChannel extends Component {
@service chat; @service chat;
@service chatStateManager; @service chatStateManager;
@service chatChannelsManager; @service chatChannelsManager;
@service chatHistory;
get backBtnRoute() { get backBtnRoute() {
if (this.chat.activeChannel?.isDirectMessageChannel) { if (this.chatHistory.previousRoute?.name === "chat.browse") {
return "chat.browse";
} else if (this.chat.activeChannel?.isDirectMessageChannel) {
return "chat.direct-messages"; return "chat.direct-messages";
} else { } else {
return "chat.channels"; return "chat.channels";
@ -34,34 +37,36 @@ export default class ChatDrawerRoutesChannel extends Component {
} }
<template> <template>
<Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|> <div class="c-drawer-routes --channel">
<navbar.BackButton @route={{this.backBtnRoute}} /> <Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|>
<navbar.ChannelTitle @channel={{this.chat.activeChannel}} /> <navbar.BackButton @route={{this.backBtnRoute}} />
<navbar.Actions as |a|> <navbar.ChannelTitle @channel={{this.chat.activeChannel}} />
<a.ThreadsListButton @channel={{this.chat.activeChannel}} /> <navbar.Actions as |a|>
<a.ToggleDrawerButton /> <a.ThreadsListButton @channel={{this.chat.activeChannel}} />
<a.FullPageButton /> <a.ToggleDrawerButton />
<a.CloseDrawerButton /> <a.FullPageButton />
</navbar.Actions> <a.CloseDrawerButton />
</Navbar> </navbar.Actions>
</Navbar>
{{#if this.chatStateManager.isDrawerExpanded}} {{#if this.chatStateManager.isDrawerExpanded}}
<div <div
class="chat-drawer-content" class="chat-drawer-content"
{{didInsert this.fetchChannel}} {{didInsert this.fetchChannel}}
{{didUpdate this.fetchChannel @params.channelId}} {{didUpdate this.fetchChannel @params.channelId}}
> >
{{#if this.chat.activeChannel}} {{#if this.chat.activeChannel}}
{{#each (array this.chat.activeChannel) as |channel|}} {{#each (array this.chat.activeChannel) as |channel|}}
{{#if channel}} {{#if channel}}
<ChatChannel <ChatChannel
@targetMessageId={{readonly @params.messageId}} @targetMessageId={{readonly @params.messageId}}
@channel={{channel}} @channel={{channel}}
/> />
{{/if}} {{/if}}
{{/each}} {{/each}}
{{/if}} {{/if}}
</div> </div>
{{/if}} {{/if}}
</div>
</template> </template>
} }

View File

@ -10,21 +10,23 @@ export default class ChatDrawerRoutesChannels extends Component {
@service chatStateManager; @service chatStateManager;
<template> <template>
<Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|> <div class="c-drawer-routes --channels">
<navbar.Title @title={{i18n "chat.heading"}} /> <Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|>
<navbar.Actions as |a|> <navbar.Title @title={{i18n "chat.heading"}} />
<a.ToggleDrawerButton /> <navbar.Actions as |a|>
<a.FullPageButton /> <a.ToggleDrawerButton />
<a.CloseDrawerButton /> <a.FullPageButton />
</navbar.Actions> <a.CloseDrawerButton />
</Navbar> </navbar.Actions>
</Navbar>
{{#if this.chatStateManager.isDrawerExpanded}} {{#if this.chatStateManager.isDrawerExpanded}}
<div class="chat-drawer-content"> <div class="chat-drawer-content">
<ChannelsListPublic /> <ChannelsListPublic />
</div> </div>
<ChatFooter /> <ChatFooter />
{{/if}} {{/if}}
</div>
</template> </template>
} }

View File

@ -5,26 +5,28 @@ import ChannelsListDirect from "discourse/plugins/chat/discourse/components/chan
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar"; import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
import ChatFooter from "discourse/plugins/chat/discourse/components/chat-footer"; import ChatFooter from "discourse/plugins/chat/discourse/components/chat-footer";
export default class ChatDrawerRoutesChannels extends Component { export default class ChatDrawerRoutesDirectMessages extends Component {
@service chat; @service chat;
@service chatStateManager; @service chatStateManager;
<template> <template>
<Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|> <div class="c-drawer-routes --direct-messages">
<navbar.Title @title={{i18n "chat.heading"}} /> <Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|>
<navbar.Actions as |a|> <navbar.Title @title={{i18n "chat.heading"}} />
<a.ToggleDrawerButton /> <navbar.Actions as |a|>
<a.FullPageButton /> <a.ToggleDrawerButton />
<a.CloseDrawerButton /> <a.FullPageButton />
</navbar.Actions> <a.CloseDrawerButton />
</Navbar> </navbar.Actions>
</Navbar>
{{#if this.chatStateManager.isDrawerExpanded}} {{#if this.chatStateManager.isDrawerExpanded}}
<div class="chat-drawer-content"> <div class="chat-drawer-content">
<ChannelsListDirect /> <ChannelsListDirect />
</div> </div>
<ChatFooter /> <ChatFooter />
{{/if}} {{/if}}
</div>
</template> </template>
} }

View File

@ -10,22 +10,24 @@ export default class ChatDrawerRoutesThreads extends Component {
@service chatStateManager; @service chatStateManager;
<template> <template>
<Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|> <div class="c-drawer-routes --threads">
<navbar.Title @title={{i18n "chat.heading"}} /> <Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|>
<navbar.Actions as |action|> <navbar.Title @title={{i18n "chat.heading"}} />
<action.ThreadsListButton /> <navbar.Actions as |action|>
<action.ToggleDrawerButton /> <action.ThreadsListButton />
<action.FullPageButton /> <action.ToggleDrawerButton />
<action.CloseDrawerButton /> <action.FullPageButton />
</navbar.Actions> <action.CloseDrawerButton />
</Navbar> </navbar.Actions>
</Navbar>
{{#if this.chatStateManager.isDrawerExpanded}} {{#if this.chatStateManager.isDrawerExpanded}}
<div class="chat-drawer-content"> <div class="chat-drawer-content">
<UserThreads /> <UserThreads />
</div> </div>
{{/if}} {{/if}}
<ChatFooter /> <ChatFooter />
</div>
</template> </template>
} }

View File

@ -2,7 +2,6 @@ import Component from "@glimmer/component";
import { LinkTo } from "@ember/routing"; import { LinkTo } from "@ember/routing";
import icon from "discourse-common/helpers/d-icon"; import icon from "discourse-common/helpers/d-icon";
import I18n from "I18n"; import I18n from "I18n";
import { FOOTER_NAV_ROUTES } from "discourse/plugins/chat/discourse/lib/chat-constants";
export default class ChatNavbarBackButton extends Component { export default class ChatNavbarBackButton extends Component {
get icon() { get icon() {
@ -14,11 +13,7 @@ export default class ChatNavbarBackButton extends Component {
} }
get targetRoute() { get targetRoute() {
if (FOOTER_NAV_ROUTES.includes(this.args.route)) { return this.args.route ?? "chat";
return this.args.route;
} else {
return "chat";
}
} }
<template> <template>

View File

@ -1,131 +0,0 @@
import { cached, tracked } from "@glimmer/tracking";
import Component from "@ember/component";
import { concat, hash } from "@ember/helper";
import { action, computed } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { LinkTo } from "@ember/routing";
import { schedule } from "@ember/runloop";
import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import { INPUT_DELAY } from "discourse-common/config/environment";
import i18n from "discourse-common/helpers/i18n";
import discourseDebounce from "discourse-common/lib/debounce";
import List from "discourse/plugins/chat/discourse/components/chat/list";
import ChatModalNewMessage from "discourse/plugins/chat/discourse/components/chat/modal/new-message";
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
import ChatChannelCard from "discourse/plugins/chat/discourse/components/chat-channel-card";
import DcFilterInput from "discourse/plugins/chat/discourse/components/dc-filter-input";
const TABS = ["all", "open", "closed", "archived"];
export default class ChatRoutesBrowse extends Component {
@service chatApi;
@service modal;
@tracked filter = "";
@cached
get channelsCollection() {
return this.chatApi.channels({
filter: this.filter,
status: this.status,
});
}
@computed("siteSettings.chat_allow_archiving_channels")
get tabs() {
if (this.siteSettings.chat_allow_archiving_channels) {
return TABS;
} else {
return [...TABS].removeObject("archived");
}
}
@action
showChatNewMessageModal() {
this.modal.show(ChatModalNewMessage);
}
@action
setFilter(event) {
this.filter = event.target.value;
discourseDebounce(this.debouncedLoad, INPUT_DELAY);
}
@action
debouncedLoad() {
this.channelsCollection.load({ limit: 10 });
}
@action
focusFilterInput(input) {
schedule("afterRender", () => input?.focus());
}
<template>
<div class="c-routes-browse">
<Navbar as |navbar|>
<navbar.BackButton />
<navbar.Title @title={{i18n "chat.browse.title"}} />
<navbar.Actions as |a|>
<a.NewChannelButton />
</navbar.Actions>
</Navbar>
<div class="chat-browse-view">
<div class="chat-browse-view__actions">
<nav>
<ul class="nav-pills chat-browse-view__filters">
{{#each this.tabs as |tab|}}
<li class={{concat "chat-browse-view__filter -" tab}}>
<LinkTo
@route={{concat "chat.browse." tab}}
class={{concat "chat-browse-view__filter-link -" tab}}
>
{{i18n (concat "chat.browse.filter_" tab)}}
</LinkTo>
</li>
{{/each}}
</ul>
</nav>
<DcFilterInput
{{didInsert this.focusFilterInput}}
@filterAction={{this.setFilter}}
@icons={{hash right="search"}}
@containerClass="filter-input"
placeholder={{i18n "chat.browse.filter_input_placeholder"}}
/>
</div>
<div class="chat-browse-view__content_wrapper">
<div class="chat-browse-view__content">
<List
@collection={{this.channelsCollection}}
class="chat-browse-view__cards"
as |list|
>
<list.Item as |channel|>
<ChatChannelCard @channel={{channel}} />
</list.Item>
<list.EmptyState>
<span class="empty-state-title">
{{i18n "chat.empty_state.title"}}
</span>
<div class="empty-state-body">
<p>{{i18n "chat.empty_state.direct_message"}}</p>
<DButton
@action={{this.showChatNewMessageModal}}
@label="chat.empty_state.direct_message_cta"
/>
</div>
</list.EmptyState>
</List>
</div>
</div>
</div>
</div>
</template>
}

View File

@ -125,76 +125,78 @@ export default class ChatRouteChannelInfoMembers extends Component {
} }
<template> <template>
{{#if this.site.mobileView}} <div class="c-routes --channel-info-members">
<LinkTo {{#if this.site.mobileView}}
class="c-back-button" <LinkTo
@route="chat.channel.info.settings" class="c-back-button"
@model={{@channel}} @route="chat.channel.info.settings"
> @model={{@channel}}
{{icon "chevron-left"}} >
{{i18n "chat.members_view.back_to_settings"}} {{icon "chevron-left"}}
</LinkTo> {{i18n "chat.members_view.back_to_settings"}}
{{/if}} </LinkTo>
{{#if this.showAddMembers}} {{/if}}
<MessageCreator {{#if this.showAddMembers}}
@mode={{this.addMembersMode}} <MessageCreator
@channel={{@channel}} @mode={{this.addMembersMode}}
@onClose={{this.hideAddMember}} @channel={{@channel}}
@onCancel={{this.hideAddMember}} @onClose={{this.hideAddMember}}
/> @onCancel={{this.hideAddMember}}
{{else}}
<div class="c-channel-members">
<DcFilterInput
{{autoFocus}}
@filterAction={{this.mutFilter}}
@icons={{hash right="search"}}
@containerClass="c-channel-members__filter"
placeholder={{this.filterPlaceholder}}
/> />
{{else}}
<div class="c-channel-members">
<DcFilterInput
{{autoFocus}}
@filterAction={{this.mutFilter}}
@icons={{hash right="search"}}
@containerClass="c-channel-members__filter"
placeholder={{this.filterPlaceholder}}
/>
<ul class="c-channel-members__list" {{this.fill}}> <ul class="c-channel-members__list" {{this.fill}}>
{{#if @channel.chatable.group}} {{#if @channel.chatable.group}}
<li
class="c-channel-members__list-item -add-member"
role="button"
{{on "click" this.addMember}}
{{this.onEnter this.addMember}}
tabindex="0"
>
{{icon "plus"}}
<span>{{this.addMemberLabel}}</span>
</li>
{{/if}}
{{#each this.members as |membership|}}
<li
class="c-channel-members__list-item -member"
{{on "click" (fn this.openMemberCard membership.user)}}
{{this.onEnter (fn this.openMemberCard membership.user)}}
tabindex="0"
>
<ChatUserInfo
@user={{membership.user}}
@avatarSize="tiny"
@interactive={{false}}
@showStatus={{true}}
@showStatusDescription={{true}}
/>
</li>
{{else}}
{{#if this.noResults}}
<li <li
class="c-channel-members__list-item -no-results alert alert-info" class="c-channel-members__list-item -add-member"
role="button"
{{on "click" this.addMember}}
{{this.onEnter this.addMember}}
tabindex="0"
> >
{{this.noMembershipsFoundLabel}} {{icon "plus"}}
<span>{{this.addMemberLabel}}</span>
</li> </li>
{{/if}} {{/if}}
{{/each}} {{#each this.members as |membership|}}
</ul> <li
class="c-channel-members__list-item -member"
{{on "click" (fn this.openMemberCard membership.user)}}
{{this.onEnter (fn this.openMemberCard membership.user)}}
tabindex="0"
>
<ChatUserInfo
@user={{membership.user}}
@avatarSize="tiny"
@interactive={{false}}
@showStatus={{true}}
@showStatusDescription={{true}}
/>
</li>
{{else}}
{{#if this.noResults}}
<li
class="c-channel-members__list-item -no-results alert alert-info"
>
{{this.noMembershipsFoundLabel}}
</li>
{{/if}}
{{/each}}
</ul>
<div {{this.loadMore}}> <div {{this.loadMore}}>
<br /> <br />
</div>
</div> </div>
</div> {{/if}}
{{/if}} </div>
</template> </template>
} }

View File

@ -12,36 +12,38 @@ export default class ChatRoutesChannelInfoNav extends Component {
} }
<template> <template>
{{#if this.showTabs}} <div class="c-routes --channel-info-nav">
<nav class="c-channel-info__nav"> {{#if this.showTabs}}
<ul class="nav nav-pills"> <nav class="c-channel-info__nav">
<li> <ul class="nav nav-pills">
<LinkTo <li>
@route="chat.channel.info.settings" <LinkTo
@models={{@channel.routeModels}} @route="chat.channel.info.settings"
class={{if (eq @tab "settings") "active"}} @models={{@channel.routeModels}}
@replace={{true}} class={{if (eq @tab "settings") "active"}}
> @replace={{true}}
{{i18n "chat.channel_info.tabs.settings"}} >
</LinkTo> {{i18n "chat.channel_info.tabs.settings"}}
</li> </LinkTo>
<li> </li>
<LinkTo <li>
@route="chat.channel.info.members" <LinkTo
@models={{@channel.routeModels}} @route="chat.channel.info.members"
class={{if (eq @tab "members") "active"}} @models={{@channel.routeModels}}
@replace={{true}} class={{if (eq @tab "members") "active"}}
> @replace={{true}}
{{i18n "chat.channel_info.tabs.members"}} >
{{#if @channel.isCategoryChannel}} {{i18n "chat.channel_info.tabs.members"}}
<span {{#if @channel.isCategoryChannel}}
class="c-channel-info__member-count" <span
>({{@channel.membershipsCount}})</span> class="c-channel-info__member-count"
{{/if}} >({{@channel.membershipsCount}})</span>
</LinkTo> {{/if}}
</li> </LinkTo>
</ul> </li>
</nav> </ul>
{{/if}} </nav>
{{/if}}
</div>
</template> </template>
} }

View File

@ -327,280 +327,283 @@ export default class ChatRouteChannelInfoSettings extends Component {
} }
<template> <template>
<div class="c-channel-settings"> <div class="c-routes --channel-info-settings">
<ChatForm as |form|> <div class="c-channel-settings">
<form.section @title={{this.titleSectionTitle}} as |section|> <ChatForm as |form|>
<section.row> <form.section @title={{this.titleSectionTitle}} as |section|>
<:default>
<div class="c-channel-settings__name">
{{replaceEmoji @channel.title}}
</div>
{{#if @channel.isCategoryChannel}}
<div class="c-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.onEditChannelTitle}}
class="edit-name-slug-btn btn-flat"
/>
{{/if}}
</:action>
</section.row>
</form.section>
{{#if this.shouldRenderDescriptionSection}}
<form.section @title={{this.descriptionSectionTitle}} as |section|>
<section.row> <section.row>
<:default> <:default>
{{#if @channel.description.length}} <div class="c-channel-settings__name">
{{@channel.description}} {{replaceEmoji @channel.title}}
{{else}} </div>
{{this.descriptionPlaceholder}}
{{#if @channel.isCategoryChannel}}
<div class="c-channel-settings__slug">
<LinkTo
@route="chat.channel"
@models={{@channel.routeModels}}
>
/chat/c/{{@channel.slug}}/{{@channel.id}}
</LinkTo>
</div>
{{/if}} {{/if}}
</:default> </:default>
<:action> <:action>
{{#if this.canEditChannel}} {{#if this.canEditChannel}}
<DButton <DButton
@label={{if @label="chat.channel_settings.edit"
@channel.description.length @action={{this.onEditChannelTitle}}
"chat.channel_settings.edit" class="edit-name-slug-btn btn-flat"
"chat.channel_settings.add"
}}
@action={{this.onEditChannelDescription}}
class="edit-description-btn btn-flat"
/> />
{{/if}} {{/if}}
</:action> </:action>
</section.row> </section.row>
</form.section> </form.section>
{{/if}}
{{#if this.site.mobileView}} {{#if this.shouldRenderDescriptionSection}}
<form.section as |section|> <form.section @title={{this.descriptionSectionTitle}} as |section|>
<section.row <section.row>
@label={{this.membersLabel}} <:default>
@route="chat.channel.info.members" {{#if @channel.description.length}}
@routeModels={{@channel.routeModels}} {{@channel.description}}
/> {{else}}
</form.section> {{this.descriptionPlaceholder}}
{{/if}} {{/if}}
</:default>
{{#if @channel.isOpen}}
<form.section @title={{this.settingsSectionTitle}} as |section|>
<section.row @label={{this.muteSectionLabel}}>
<:action>
<DToggleSwitch
@state={{@channel.currentUserMembership.muted}}
class="c-channel-settings__mute-switch"
{{on "click" this.onToggleMuted}}
/>
</:action>
</section.row>
{{#if this.shouldRenderDesktopNotificationsLevelSection}}
<section.row @label={{this.desktopNotificationsLevelLabel}}>
<:action> <:action>
<ComboBox {{#if this.canEditChannel}}
@content={{this.notificationLevels}} <DButton
@value={{@channel.currentUserMembership.desktopNotificationLevel}} @label={{if
@valueProperty="value" @channel.description.length
@onChange={{fn "chat.channel_settings.edit"
this.saveNotificationSettings "chat.channel_settings.add"
"desktopNotificationLevel" }}
"desktop_notification_level" @action={{this.onEditChannelDescription}}
}} class="edit-description-btn btn-flat"
class="c-channel-settings__selector c-channel-settings__desktop-notifications-selector" />
/> {{/if}}
</:action> </:action>
</section.row> </section.row>
{{/if}} </form.section>
{{#if this.shouldRenderMobileNotificationsLevelSection}}
<section.row @label={{this.mobileNotificationsLevelLabel}}>
<:action>
<ComboBox
@content={{this.notificationLevels}}
@value={{@channel.currentUserMembership.mobileNotificationLevel}}
@valueProperty="value"
@onChange={{fn
this.saveNotificationSettings
"mobileNotificationLevel"
"mobile_notification_level"
}}
class="c-channel-settings__selector c-channel-settings__mobile-notifications-selector"
/>
</: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}} {{/if}}
<section.row @label={{this.historyLabel}}> {{#if this.site.mobileView}}
<ChatRetentionReminderText @channel={{@channel}} @type="short" /> <form.section as |section|>
</section.row> <section.row
</form.section> @label={{this.membersLabel}}
@route="chat.channel.info.members"
@routeModels={{@channel.routeModels}}
/>
</form.section>
{{/if}}
{{#if this.shouldRenderAdminSection}} {{#if @channel.isOpen}}
<form.section <form.section @title={{this.settingsSectionTitle}} as |section|>
@title={{this.adminSectionTitle}} <section.row @label={{this.muteSectionLabel}}>
data-section="admin"
as |section|
>
{{#if this.autoJoinAvailable}}
<section.row @label={{this.autoJoinLabel}}>
<:action> <:action>
<DToggleSwitch <DToggleSwitch
@state={{@channel.autoJoinUsers}} @state={{@channel.currentUserMembership.muted}}
class="c-channel-settings__auto-join-switch" class="c-channel-settings__mute-switch"
{{on {{on "click" this.onToggleMuted}}
"click"
(fn this.onToggleAutoJoinUsers @channel.autoJoinUsers)
}}
/> />
</:action> </:action>
</section.row> </section.row>
{{/if}}
{{#if this.toggleChannelWideMentionsAvailable}} {{#if this.shouldRenderDesktopNotificationsLevelSection}}
<section.row @label={{this.channelWideMentionsLabel}}> <section.row @label={{this.desktopNotificationsLevelLabel}}>
<:action>
<DToggleSwitch
class="c-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="c-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> <:action>
<DButton <ComboBox
@action={{this.onArchiveChannel}} @content={{this.notificationLevels}}
@label="chat.channel_settings.archive_channel" @value={{@channel.currentUserMembership.desktopNotificationLevel}}
@icon="archive" @valueProperty="value"
class="archive-btn chat-form__btn btn-transparent" @onChange={{fn
this.saveNotificationSettings
"desktopNotificationLevel"
"desktop_notification_level"
}}
class="c-channel-settings__selector c-channel-settings__desktop-notifications-selector"
/> />
</:action> </:action>
</section.row> </section.row>
{{/if}} {{/if}}
<section.row> {{#if this.shouldRenderMobileNotificationsLevelSection}}
<:action> <section.row @label={{this.mobileNotificationsLevelLabel}}>
{{#if @channel.isOpen}} <:action>
<DButton <ComboBox
@action={{this.onToggleChannelState}} @content={{this.notificationLevels}}
@label="chat.channel_settings.close_channel" @value={{@channel.currentUserMembership.mobileNotificationLevel}}
@icon="lock" @valueProperty="value"
class="close-btn chat-form__btn btn-transparent" @onChange={{fn
this.saveNotificationSettings
"mobileNotificationLevel"
"mobile_notification_level"
}}
class="c-channel-settings__selector c-channel-settings__mobile-notifications-selector"
/> />
{{else}} </:action>
<DButton </section.row>
@action={{this.onToggleChannelState}} {{/if}}
@label="chat.channel_settings.open_channel" </form.section>
@icon="unlock" {{/if}}
class="open-btn chat-form__btn btn-transparent"
/>
{{/if}}
</:action>
</section.row>
<section.row> <form.section @title={{this.channelInfoSectionTitle}} as |section|>
<:action> {{#if @channel.isCategoryChannel}}
<DButton <section.row @label={{this.categoryLabel}}>
@action={{this.onDeleteChannel}} {{categoryBadge
@label="chat.channel_settings.delete_channel" @channel.chatable
@icon="trash-alt" link=true
class="delete-btn chat-form__btn btn-transparent" allowUncategorized=true
/> }}
</:action>
</section.row> </section.row>
{{/if}} {{/if}}
<section.row @label={{this.historyLabel}}>
<ChatRetentionReminderText @channel={{@channel}} @type="short" />
</section.row>
</form.section> </form.section>
{{/if}}
<form.section class="--leave-channel" as |section|> {{#if this.shouldRenderAdminSection}}
{{#if @channel.chatable.group}} <form.section
<div class="c-channel-settings__leave-info"> @title={{this.adminSectionTitle}}
{{icon "exclamation-triangle"}} data-section="admin"
{{i18n "chat.channel_settings.leave_groupchat_info"}} as |section|
</div> >
{{#if this.autoJoinAvailable}}
<section.row @label={{this.autoJoinLabel}}>
<:action>
<DToggleSwitch
@state={{@channel.autoJoinUsers}}
class="c-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="c-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="c-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-transparent"
/>
</: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-transparent"
/>
{{else}}
<DButton
@action={{this.onToggleChannelState}}
@label="chat.channel_settings.open_channel"
@icon="unlock"
class="open-btn chat-form__btn btn-transparent"
/>
{{/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-transparent"
/>
</:action>
</section.row>
{{/if}}
</form.section>
{{/if}} {{/if}}
<section.row>
<:action> <form.section class="--leave-channel" as |section|>
<ToggleChannelMembershipButton {{#if @channel.chatable.group}}
@channel={{@channel}} <div class="c-channel-settings__leave-info">
@onLeave={{this.onLeaveChannel}} {{icon "exclamation-triangle"}}
@options={{hash {{i18n "chat.channel_settings.leave_groupchat_info"}}
joinClass="btn-primary" </div>
leaveClass="btn-danger" {{/if}}
joinIcon="sign-in-alt" <section.row>
leaveIcon="sign-out-alt" <:action>
}} <ToggleChannelMembershipButton
/> @channel={{@channel}}
</:action> @onLeave={{this.onLeaveChannel}}
</section.row> @options={{hash
</form.section> joinClass="btn-primary"
</ChatForm> leaveClass="btn-danger"
joinIcon="sign-in-alt"
leaveIcon="sign-out-alt"
}}
/>
</:action>
</section.row>
</form.section>
</ChatForm>
</div>
</div> </div>
</template> </template>
} }

View File

@ -30,7 +30,7 @@ export default class ChatRoutesChannelInfo extends Component {
} }
<template> <template>
<div class="c-routes-channel-info"> <div class="c-routes --channel-info">
<Navbar as |navbar|> <Navbar as |navbar|>
{{#if this.chatChannelInfoRouteOriginManager.isBrowse}} {{#if this.chatChannelInfoRouteOriginManager.isBrowse}}
<navbar.BackButton <navbar.BackButton

View File

@ -14,7 +14,7 @@ export default class ChatRoutesChannelThread extends Component {
} }
<template> <template>
<div class="c-routes-channel-thread"> <div class="c-routes --channel-thread">
{{#each (array @thread) as |thread|}} {{#each (array @thread) as |thread|}}
<ThreadHeader <ThreadHeader
@thread={{thread}} @thread={{thread}}

View File

@ -2,7 +2,7 @@ import ChatThreadListHeader from "discourse/plugins/chat/discourse/components/ch
import ChatThreadList from "discourse/plugins/chat/discourse/components/chat-thread-list"; import ChatThreadList from "discourse/plugins/chat/discourse/components/chat-thread-list";
const ChatRoutesChannelThreads = <template> const ChatRoutesChannelThreads = <template>
<div class="c-routes-channel-threads"> <div class="c-routes --channel-threads">
<ChatThreadListHeader @channel={{@channel}} /> <ChatThreadListHeader @channel={{@channel}} />
<ChatThreadList @channel={{@channel}} @includeHeader={{true}} /> <ChatThreadList @channel={{@channel}} @includeHeader={{true}} />
</div> </div>

View File

@ -14,7 +14,7 @@ export default class ChatRoutesChannel extends Component {
} }
<template> <template>
<div class="c-routes-channel"> <div class="c-routes --channel">
<Navbar as |navbar|> <Navbar as |navbar|>
{{#if this.site.mobileView}} {{#if this.site.mobileView}}
<navbar.BackButton @route={{this.getChannelsRoute}} /> <navbar.BackButton @route={{this.getChannelsRoute}} />

View File

@ -8,7 +8,7 @@ export default class ChatRoutesChannels extends Component {
@service site; @service site;
<template> <template>
<div class="c-routes-channels"> <div class="c-routes --channels">
<Navbar as |navbar|> <Navbar as |navbar|>
<navbar.Title @title={{i18n "chat.chat_channels"}} /> <navbar.Title @title={{i18n "chat.chat_channels"}} />
<navbar.Actions as |action|> <navbar.Actions as |action|>

View File

@ -8,7 +8,7 @@ export default class ChatRoutesDirectMessages extends Component {
@service site; @service site;
<template> <template>
<div class="c-routes-direct-messages"> <div class="c-routes --direct-messages">
<Navbar as |navbar|> <Navbar as |navbar|>
<navbar.Title @title={{i18n "chat.direct_messages.title"}} /> <navbar.Title @title={{i18n "chat.direct_messages.title"}} />
<navbar.Actions as |action|> <navbar.Actions as |action|>

View File

@ -8,7 +8,7 @@ export default class ChatRoutesThreads extends Component {
@service site; @service site;
<template> <template>
<div class="c-routes-threads"> <div class="c-routes --threads">
<Navbar as |navbar|> <Navbar as |navbar|>
<navbar.Title @title={{i18n "chat.my_threads.title"}} /> <navbar.Title @title={{i18n "chat.my_threads.title"}} />

View File

@ -78,10 +78,16 @@ export default class ChatChannelComposer extends Service {
message.channel.resetDraft(this.currentUser); message.channel.resetDraft(this.currentUser);
await this.router.transitionTo( try {
"chat.channel.thread", await this.router.transitionTo(
...message.thread.routeModels "chat.channel.thread",
); ...message.thread.routeModels
);
} catch (e) {
if (e.name !== "TransitionAborted") {
throw e;
}
}
this.threadComposer.focus({ ensureAtEnd: true, refreshHeight: true }); this.threadComposer.focus({ ensureAtEnd: true, refreshHeight: true });
} else { } else {

View File

@ -1,16 +1,43 @@
import { tracked } from "@glimmer/tracking"; import { tracked } from "@glimmer/tracking";
import Service, { service } from "@ember/service"; import Service, { service } from "@ember/service";
import ChatDrawerRoutesBrowse from "discourse/plugins/chat/discourse/components/chat/drawer-routes/browse";
import ChatDrawerRoutesChannel from "discourse/plugins/chat/discourse/components/chat/drawer-routes/channel"; import ChatDrawerRoutesChannel from "discourse/plugins/chat/discourse/components/chat/drawer-routes/channel";
import ChatDrawerRoutesChannelInfoMembers from "discourse/plugins/chat/discourse/components/chat/drawer-routes/channel-info-members";
import ChatDrawerRoutesChannelInfoSettings from "discourse/plugins/chat/discourse/components/chat/drawer-routes/channel-info-settings";
import ChatDrawerRoutesChannelThread from "discourse/plugins/chat/discourse/components/chat/drawer-routes/channel-thread"; 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 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 ChatDrawerRoutesChannels from "discourse/plugins/chat/discourse/components/chat/drawer-routes/channels";
import ChatDrawerRoutesDirectMessages from "discourse/plugins/chat/discourse/components/chat/drawer-routes/direct-messages"; import ChatDrawerRoutesDirectMessages from "discourse/plugins/chat/discourse/components/chat/drawer-routes/direct-messages";
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"; import ChatDrawerRoutesThreads from "discourse/plugins/chat/discourse/components/chat/drawer-routes/threads";
const ROUTES = { const ROUTES = {
chat: { name: ChatDrawerRoutesChannels },
"chat.index": { name: ChatDrawerRoutesChannels }, "chat.index": { name: ChatDrawerRoutesChannels },
// order matters, non index before index
"chat.browse": {
name: ChatDrawerRoutesBrowse,
extractParams: () => ({ currentTab: "all" }),
},
"chat.browse.index": {
name: ChatDrawerRoutesBrowse,
extractParams: () => ({ currentTab: "all" }),
},
"chat.browse.open": {
name: ChatDrawerRoutesBrowse,
extractParams: (r) => ({ currentTab: r.localName }),
},
"chat.browse.archived": {
name: ChatDrawerRoutesBrowse,
extractParams: (r) => ({ currentTab: r.localName }),
},
"chat.browse.closed": {
name: ChatDrawerRoutesBrowse,
extractParams: (r) => ({ currentTab: r.localName }),
},
"chat.browse.all": {
name: ChatDrawerRoutesBrowse,
extractParams: (r) => ({ currentTab: r.localName }),
},
"chat.channels": { name: ChatDrawerRoutesChannels }, "chat.channels": { name: ChatDrawerRoutesChannels },
"chat.channel": { name: ChatDrawerRoutesChannel }, "chat.channel": { name: ChatDrawerRoutesChannel },
"chat.channel.index": { name: ChatDrawerRoutesChannel }, "chat.channel.index": { name: ChatDrawerRoutesChannel },
@ -56,7 +83,6 @@ const ROUTES = {
"chat.threads": { "chat.threads": {
name: ChatDrawerRoutesThreads, name: ChatDrawerRoutesThreads,
}, },
chat: { name: ChatDrawerRoutesChannels },
"chat.channel.near-message": { "chat.channel.near-message": {
name: ChatDrawerRoutesChannel, name: ChatDrawerRoutesChannel,
extractParams: (route) => { extractParams: (route) => {
@ -76,7 +102,7 @@ const ROUTES = {
}, },
}, },
"chat.channel.info.settings": { "chat.channel.info.settings": {
name: ChatDrawerRoutesSettings, name: ChatDrawerRoutesChannelInfoSettings,
extractParams: (route) => { extractParams: (route) => {
return { return {
channelId: route.parent.params.channelId, channelId: route.parent.params.channelId,
@ -84,7 +110,7 @@ const ROUTES = {
}, },
}, },
"chat.channel.info.members": { "chat.channel.info.members": {
name: ChatDrawerRoutesMembers, name: ChatDrawerRoutesChannelInfoMembers,
extractParams: (route) => { extractParams: (route) => {
return { return {
channelId: route.parent.params.channelId, channelId: route.parent.params.channelId,

View File

@ -1 +1 @@
<Chat::Routes::Browse @status="all" /> <BrowseChannels @currentTab="all" />

View File

@ -1 +1 @@
<Chat::Routes::Browse @status="archived" /> <BrowseChannels @currentTab="archived" />

View File

@ -1 +1 @@
<Chat::Routes::Browse @status="closed" /> <BrowseChannels @currentTab="closed" />

View File

@ -1 +1 @@
<Chat::Routes::Browse @status="open" /> <BrowseChannels @currentTab="open" />

View File

@ -1 +1,13 @@
{{outlet}} <div class="c-routes --browse">
<Chat::Navbar as |navbar|>
<navbar.BackButton />
<navbar.Title @title={{i18n "chat.browse.title"}} />
<navbar.Actions as |a|>
<a.NewChannelButton />
<a.OpenDrawerButton />
</navbar.Actions>
</Chat::Navbar>
{{outlet}}
</div>

View File

@ -35,6 +35,16 @@
justify-content: space-between; justify-content: space-between;
align-items: end; align-items: end;
.c-drawer-routes.--browse & {
flex-direction: column;
gap: 1rem;
align-items: flex-start;
.filter-input {
width: 100%;
}
}
@include breakpoint(tablet) { @include breakpoint(tablet) {
flex-direction: column; flex-direction: column;

View File

@ -0,0 +1,6 @@
.c-drawer-routes {
display: flex;
flex-direction: column;
flex: 1 0 auto;
height: 100%;
}

View File

@ -2,6 +2,7 @@
@import "chat-height-mixin"; @import "chat-height-mixin";
@import "base-common"; @import "base-common";
@import "sidebar-extensions"; @import "sidebar-extensions";
@import "chat-drawer-routes";
@import "chat-browse"; @import "chat-browse";
@import "chat-channel"; @import "chat-channel";
@import "chat-channel-card"; @import "chat-channel-card";

View File

@ -1,7 +1,7 @@
.c-navbar-container { .c-navbar-container {
.chat-drawer &, .chat-drawer &,
.c-routes-channel-thread &, .c-routes-channel.--thread &,
.c-routes-channel-info & { .c-routes-channel.--info & {
padding-inline: 0; padding-inline: 0;
} }
} }

View File

@ -21,7 +21,7 @@ html.has-full-page-chat {
&.has-side-panel-expanded { &.has-side-panel-expanded {
grid-template-columns: 1fr; grid-template-columns: 1fr;
.c-routes-channel { .c-routes.--channel {
display: none; display: none;
} }
} }

View File

@ -8,9 +8,9 @@
} }
} }
.c-routes-direct-messages, .c-routes.--direct-messages,
.c-routes-channels, .c-routes.--channels,
.c-routes-threads { .c-routes.--threads {
background: var(--primary-very-low); background: var(--primary-very-low);
max-width: 100vw; max-width: 100vw;
} }

View File

@ -1,5 +1,5 @@
.chat-message-thread-indicator { .chat-message-thread-indicator {
.c-routes-threads & { .c-routes.--threads & {
background: var(--secondary); background: var(--secondary);
} }
grid-template-areas: grid-template-areas:

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
RSpec.describe "Drawer - Browse", type: :system do
fab!(:current_user) { Fabricate(:user) }
let(:drawer_page) { PageObjects::Pages::ChatDrawer.new }
fab!(:channel_1) { Fabricate(:chat_channel) }
before do
chat_system_bootstrap
sign_in(current_user)
end
it "can change status" do
drawer_page.visit_browse
expect(drawer_page.browse).to have_channel(name: channel_1.name)
drawer_page.browse.change_status("closed")
expect(drawer_page.browse).to have_no_channel(name: channel_1.name)
end
end

View File

@ -42,4 +42,13 @@ RSpec.describe "Drawer - index", type: :system do
expect(row).to be_non_existent expect(row).to be_non_existent
end end
it "can open browse" do
channel = Fabricate(:chat_channel)
drawer_page.visit_index
drawer_page.channels_index.open_browse
expect(drawer_page.browse).to have_channel(name: channel.name)
end
end end

View File

@ -9,8 +9,8 @@ describe "Using #hashtag autocompletion to search for and lookup channels", type
fab!(:topic) fab!(:topic)
fab!(:post) { Fabricate(:post, topic: topic) } fab!(:post) { Fabricate(:post, topic: topic) }
fab!(:message1) { Fabricate(:chat_message, chat_channel: channel1) } fab!(:message1) { Fabricate(:chat_message, chat_channel: channel1) }
let(:chat_page) { PageObjects::Pages::Chat.new } let(:chat_page) { PageObjects::Pages::Chat.new }
let(:chat_drawer_page) { PageObjects::Pages::ChatDrawer.new }
let(:chat_channel_page) { PageObjects::Pages::ChatChannel.new } let(:chat_channel_page) { PageObjects::Pages::ChatChannel.new }
let(:topic_page) { PageObjects::Pages::Topic.new } let(:topic_page) { PageObjects::Pages::Topic.new }

View File

@ -5,7 +5,7 @@ RSpec.describe "Message notifications - mobile", type: :system, mobile: true do
let!(:chat_page) { PageObjects::Pages::Chat.new } let!(:chat_page) { PageObjects::Pages::Chat.new }
let!(:chat_channel_page) { PageObjects::Pages::ChatChannel.new } let!(:chat_channel_page) { PageObjects::Pages::ChatChannel.new }
let!(:channel_index_page) { PageObjects::Components::Chat::ChannelIndex.new } let!(:channels_index_page) { PageObjects::Components::Chat::ChannelsIndex.new }
before do before do
SiteSetting.navigation_menu = "sidebar" SiteSetting.navigation_menu = "sidebar"
@ -35,7 +35,7 @@ RSpec.describe "Message notifications - mobile", type: :system, mobile: true do
create_message(channel_1, user: user_1) create_message(channel_1, user: user_1)
expect(page).to have_no_css(".chat-header-icon .chat-channel-unread-indicator") expect(page).to have_no_css(".chat-header-icon .chat-channel-unread-indicator")
expect(page).to have_no_css(channel_index_page.channel_row_selector(channel_1)) expect(page).to have_no_css(channels_index_page.channel_row_selector(channel_1))
end end
end end
end end
@ -73,7 +73,7 @@ RSpec.describe "Message notifications - mobile", type: :system, mobile: true do
create_message(channel_1, user: user_1) create_message(channel_1, user: user_1)
expect(page).to have_no_css(".chat-header-icon .chat-channel-unread-indicator") expect(page).to have_no_css(".chat-header-icon .chat-channel-unread-indicator")
expect(channel_index_page).to have_no_unread_channel(channel_1) expect(channels_index_page).to have_no_unread_channel(channel_1)
end end
end end
end end
@ -85,7 +85,7 @@ RSpec.describe "Message notifications - mobile", type: :system, mobile: true do
create_message(channel_1, user: user_1) create_message(channel_1, user: user_1)
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator", text: "") expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator", text: "")
expect(channel_index_page).to have_unread_channel(channel_1) expect(channels_index_page).to have_unread_channel(channel_1)
end end
end end
@ -102,7 +102,7 @@ RSpec.describe "Message notifications - mobile", type: :system, mobile: true do
) )
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator") expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator")
expect(channel_index_page).to have_unread_channel(channel_1, count: 1) expect(channels_index_page).to have_unread_channel(channel_1, count: 1)
end end
it "shows correct count when there are multiple messages but only 1 is urgent" do it "shows correct count when there are multiple messages but only 1 is urgent" do
@ -122,7 +122,7 @@ RSpec.describe "Message notifications - mobile", type: :system, mobile: true do
".chat-header-icon .chat-channel-unread-indicator", ".chat-header-icon .chat-channel-unread-indicator",
text: "1", text: "1",
) )
expect(channel_index_page).to have_unread_channel(channel_1, count: 1) expect(channels_index_page).to have_unread_channel(channel_1, count: 1)
end end
end end
end end
@ -147,7 +147,7 @@ RSpec.describe "Message notifications - mobile", type: :system, mobile: true do
text: "1", text: "1",
wait: 25, wait: 25,
) )
expect(channel_index_page).to have_unread_channel(dm_channel_1, wait: 25) expect(channels_index_page).to have_unread_channel(dm_channel_1, wait: 25)
create_message(dm_channel_1, user: user_1) create_message(dm_channel_1, user: user_1)
@ -198,13 +198,13 @@ RSpec.describe "Message notifications - mobile", type: :system, mobile: true do
create_message(channel_1, user: user_1) create_message(channel_1, user: user_1)
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator", text: "") expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator", text: "")
expect(channel_index_page).to have_unread_channel(channel_1) expect(channels_index_page).to have_unread_channel(channel_1)
visit("/chat/direct-messages") visit("/chat/direct-messages")
create_message(dm_channel_1, user: user_1) create_message(dm_channel_1, user: user_1)
expect(channel_index_page).to have_unread_channel(dm_channel_1) expect(channels_index_page).to have_unread_channel(dm_channel_1)
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator", text: "1") expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator", text: "1")
end end
end end

View File

@ -305,25 +305,13 @@ RSpec.describe "Navigation", type: :system do
end end
end end
context "when opening browse page from drawer in drawer mode" do
it "opens browser page in full page" do
visit("/")
chat_page.open_from_header
chat_drawer_page.open_browse
expect(page).to have_current_path("/chat/browse/open")
expect(page).not_to have_css(".chat-drawer.is-expanded")
end
end
context "when opening browse page from sidebar in drawer mode" do context "when opening browse page from sidebar in drawer mode" do
it "opens browser page in full page" do it "opens browser page in full page" do
visit("/") visit("/")
chat_page.open_from_header chat_page.open_from_header
sidebar_page.open_browse sidebar_page.open_browse
expect(page).to have_current_path("/chat/browse/open") expect(chat_drawer_page.browse).to have_channel(name: category_channel.name)
expect(page).not_to have_css(".chat-drawer.is-expanded")
end end
end end

View File

@ -3,8 +3,19 @@
module PageObjects module PageObjects
module Pages module Pages
class ChatBrowse < PageObjects::Pages::Base class ChatBrowse < PageObjects::Pages::Base
SELECTOR = ".c-routes.--browse"
def initialize(context = SELECTOR)
@context = context
end
def component def component
find(".chat-browse-view") find("#{@context} .chat-browse-view")
end
def change_status(status = "all")
component.find(".chat-browse-view__filter.-#{status}").click
has_finished_loading?
end end
def has_finished_loading? def has_finished_loading?

View File

@ -4,11 +4,11 @@ module PageObjects
module Pages module Pages
class ChatChannelThreads < PageObjects::Pages::Base class ChatChannelThreads < PageObjects::Pages::Base
def close def close
find(".c-routes-channel-threads .c-navbar__close-threads-button").click find(".c-routes.--channel-threads .c-navbar__close-threads-button").click
end end
def back def back
find(".c-routes-channel-threads .c-navbar__back-button").click find(".c-routes.--channel-threads .c-navbar__back-button").click
end end
end end
end end

View File

@ -17,7 +17,7 @@ module PageObjects
end end
def header def header
@header ||= PageObjects::Components::Chat::ThreadHeader.new(".c-routes-channel-thread") @header ||= PageObjects::Components::Chat::ThreadHeader.new(".c-routes.--channel-thread")
end end
def notifications_button def notifications_button

View File

@ -3,10 +3,10 @@
module PageObjects module PageObjects
module Components module Components
module Chat module Chat
class ChannelIndex < PageObjects::Components::Base class ChannelsIndex < PageObjects::Components::Base
attr_reader :context attr_reader :context
SELECTOR = ".channels-list-container" SELECTOR = ".c-drawer-routes.--channels"
def initialize(context = nil) def initialize(context = nil)
@context = context @context = context
@ -17,6 +17,10 @@ module PageObjects
find(context).find(SELECTOR) find(context).find(SELECTOR)
end end
def open_browse
component.find(".open-browse-page-btn").click
end
def open_channel(channel) def open_channel(channel)
component.find("#{channel_row_selector(channel)}").click component.find("#{channel_row_selector(channel)}").click
end end

View File

@ -5,7 +5,7 @@ module PageObjects
class UserThreads < PageObjects::Pages::Base class UserThreads < PageObjects::Pages::Base
def has_threads?(count: nil) def has_threads?(count: nil)
has_no_css?(".spinner") has_no_css?(".spinner")
has_css?(".c-user-thread", count: count) has_css?(".c-user-thread", count: count, visible: :all)
end end
def open_thread(thread) def open_thread(thread)

View File

@ -5,8 +5,12 @@ module PageObjects
class ChatDrawer < PageObjects::Pages::Base class ChatDrawer < PageObjects::Pages::Base
VISIBLE_DRAWER = ".chat-drawer.is-expanded" VISIBLE_DRAWER = ".chat-drawer.is-expanded"
def channel_index def channels_index
@channel_index ||= ::PageObjects::Components::Chat::ChannelIndex.new(VISIBLE_DRAWER) @channels_index ||= ::PageObjects::Components::Chat::ChannelsIndex.new(VISIBLE_DRAWER)
end
def browse
@browse ||= ::PageObjects::Pages::ChatBrowse.new(".c-drawer-routes.--browse")
end end
def open_browse def open_browse
@ -34,17 +38,22 @@ module PageObjects
open_channel(channel) open_channel(channel)
end end
def visit_browse
visit_index
open_browse
end
def open_channel(channel) def open_channel(channel)
channel_index.open_channel(channel) channels_index.open_channel(channel)
has_no_css?(".chat-skeleton") has_no_css?(".chat-skeleton")
end end
def has_unread_channel?(channel) def has_unread_channel?(channel)
channel_index.has_unread_channel?(channel) channels_index.has_unread_channel?(channel)
end end
def has_no_unread_channel?(channel) def has_no_unread_channel?(channel)
channel_index.has_no_unread_channel?(channel) channels_index.has_no_unread_channel?(channel)
end end
def has_user_threads_section? def has_user_threads_section?

View File

@ -102,7 +102,7 @@ describe "Single thread in side panel", type: :system do
it "doesnt show back button " do it "doesnt show back button " do
chat_page.visit_thread(thread) chat_page.visit_thread(thread)
expect(page).to have_no_css(".c-routes-channel-thread .c-navbar__back-button") expect(page).to have_no_css(".c-routes.--channel-thread .c-navbar__back-button")
end end
end end