FEATURE: Mobile Chat Footer Redesign (#25161)
This update adds three tabs to the bottom of the chat overlay to make it easier for users to navigate chat on mobile. As a result of this change: - Direct Messages are now shown separately from public channels on mobile - My Threads has now moved from the channel list to it's own tab on mobile - My Threads can still be accessed on desktop via the sidebar and within the drawer channel list - Chat back button has been updated to navigate to the correct tab (for both channels and threads) Some special cases: - If DMs are not used then the tab is not rendered - If the user has no threads then the tab is not rendered - If both the tabs for DMs and Threads aren't available then the whole footer will not be rendered - Chat footer is only shown on the listing pages (DMs, Channels, My Threads) --------- Co-authored-by: chapoi <101828855+chapoi@users.noreply.github.com> Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
This commit is contained in:
parent
1e57fed3b9
commit
4512e5652f
|
@ -21,4 +21,11 @@ class Chat::Api::CurrentUserThreadsController < Chat::ApiController
|
|||
on_model_not_found(:threads) { render json: success_json.merge(threads: []) }
|
||||
end
|
||||
end
|
||||
|
||||
def thread_count
|
||||
with_service(::Chat::LookupUserThreads) do
|
||||
on_success { render json: success_json.merge(thread_count: result.threads.size) }
|
||||
on_model_not_found(:threads) { render json: success_json.merge(thread_count: 0) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,8 @@ export default function () {
|
|||
});
|
||||
});
|
||||
|
||||
this.route("direct-messages", { path: "/direct-messages" });
|
||||
this.route("channels", { path: "/channels" });
|
||||
this.route("threads", { path: "/threads" });
|
||||
|
||||
this.route(
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { fn, hash } from "@ember/helper";
|
||||
import { on } from "@ember/modifier";
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||
import dIcon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import ChatModalNewMessage from "discourse/plugins/chat/discourse/components/chat/modal/new-message";
|
||||
import ChatChannelRow from "./chat-channel-row";
|
||||
|
||||
export default class ChannelsListDirect extends Component {
|
||||
@service chat;
|
||||
@service chatChannelsManager;
|
||||
@service site;
|
||||
@service modal;
|
||||
|
||||
@action
|
||||
openNewMessageModal() {
|
||||
this.modal.show(ChatModalNewMessage);
|
||||
}
|
||||
|
||||
get inSidebar() {
|
||||
return this.args.inSidebar ?? false;
|
||||
}
|
||||
|
||||
get createDirectMessageChannelLabel() {
|
||||
if (!this.canCreateDirectMessageChannel) {
|
||||
return "chat.direct_messages.cannot_create";
|
||||
}
|
||||
|
||||
return "chat.direct_messages.new";
|
||||
}
|
||||
|
||||
get showDirectMessageChannels() {
|
||||
return (
|
||||
this.canCreateDirectMessageChannel || !this.directMessageChannelsEmpty
|
||||
);
|
||||
}
|
||||
|
||||
get canCreateDirectMessageChannel() {
|
||||
return this.chat.userCanDirectMessage;
|
||||
}
|
||||
|
||||
get directMessageChannelClasses() {
|
||||
return `channels-list-container direct-message-channels ${
|
||||
this.inSidebar ? "collapsible-sidebar-section" : ""
|
||||
}`;
|
||||
}
|
||||
|
||||
get directMessageChannelsEmpty() {
|
||||
return this.chatChannelsManager.directMessageChannels?.length === 0;
|
||||
}
|
||||
|
||||
@action
|
||||
toggleChannelSection(section) {
|
||||
this.args.toggleSection(section);
|
||||
}
|
||||
|
||||
<template>
|
||||
<div
|
||||
role="region"
|
||||
aria-label={{i18n "chat.aria_roles.channels_list"}}
|
||||
class="channels-list"
|
||||
>
|
||||
|
||||
<PluginOutlet
|
||||
@name="below-direct-chat-channels"
|
||||
@tagName=""
|
||||
@outletArgs={{hash inSidebar=this.inSidebar}}
|
||||
/>
|
||||
|
||||
{{#if this.showDirectMessageChannels}}
|
||||
{{#if this.site.desktopView}}
|
||||
<div class="chat-channel-divider direct-message-channels-section">
|
||||
{{#if this.inSidebar}}
|
||||
<span
|
||||
class="title-caret"
|
||||
id="direct-message-channels-caret"
|
||||
role="button"
|
||||
title="toggle nav list"
|
||||
{{on
|
||||
"click"
|
||||
(fn this.toggleChannelSection "direct-message-channels")
|
||||
}}
|
||||
data-toggleable="direct-message-channels"
|
||||
>
|
||||
{{dIcon "angle-up"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
<span class="channel-title">{{i18n
|
||||
"chat.direct_messages.title"
|
||||
}}</span>
|
||||
|
||||
{{#if this.canCreateDirectMessageChannel}}
|
||||
<DButton
|
||||
@icon="plus"
|
||||
class="no-text btn-flat open-new-message-btn"
|
||||
@action={{this.openNewMessageModal}}
|
||||
title={{i18n this.createDirectMessageChannelLabel}}
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
<div
|
||||
id="direct-message-channels"
|
||||
class={{this.directMessageChannelClasses}}
|
||||
>
|
||||
{{#if this.directMessageChannelsEmpty}}
|
||||
<div class="channel-list-empty-message">
|
||||
<span class="channel-title">{{i18n
|
||||
"chat.no_direct_message_channels"
|
||||
}}</span>
|
||||
</div>
|
||||
{{else}}
|
||||
{{#each
|
||||
this.chatChannelsManager.truncatedDirectMessageChannels
|
||||
as |channel|
|
||||
}}
|
||||
<ChatChannelRow
|
||||
@channel={{channel}}
|
||||
@options={{hash leaveButton=true}}
|
||||
/>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { fn, hash } from "@ember/helper";
|
||||
import { on } from "@ember/modifier";
|
||||
import { action } from "@ember/object";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import { inject as service } from "@ember/service";
|
||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import dIcon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import ChatChannelRow from "./chat-channel-row";
|
||||
|
||||
export default class ChannelsListPublic extends Component {
|
||||
@service chatChannelsManager;
|
||||
@service site;
|
||||
@service siteSettings;
|
||||
@service currentUser;
|
||||
|
||||
get inSidebar() {
|
||||
return this.args.inSidebar ?? false;
|
||||
}
|
||||
|
||||
get publicMessageChannelsEmpty() {
|
||||
return this.chatChannelsManager.publicMessageChannels?.length === 0;
|
||||
}
|
||||
|
||||
get displayPublicChannels() {
|
||||
if (!this.siteSettings.enable_public_channels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.publicMessageChannelsEmpty) {
|
||||
return (
|
||||
this.currentUser?.staff ||
|
||||
this.currentUser?.has_joinable_public_channels
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
get hasUnreadThreads() {
|
||||
return this.chatChannelsManager.publicMessageChannels.some(
|
||||
(channel) => channel.unreadThreadsCount > 0
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
toggleChannelSection(section) {
|
||||
this.args.toggleSection(section);
|
||||
}
|
||||
|
||||
<template>
|
||||
<div
|
||||
role="region"
|
||||
aria-label={{i18n "chat.aria_roles.channels_list"}}
|
||||
class="channels-list"
|
||||
>
|
||||
|
||||
{{#if this.site.desktopView}}
|
||||
<LinkTo @route="chat.threads" class="chat-channel-row --threads">
|
||||
<span class="chat-channel-title">
|
||||
{{dIcon "discourse-threads" class="chat-user-threads__icon"}}
|
||||
{{i18n "chat.my_threads.title"}}
|
||||
</span>
|
||||
{{#if this.hasUnreadThreads}}
|
||||
<div class="c-unread-indicator">
|
||||
<div class="c-unread-indicator__number"> </div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.displayPublicChannels}}
|
||||
{{#if this.site.desktopView}}
|
||||
<div class="chat-channel-divider public-channels-section">
|
||||
{{#if this.inSidebar}}
|
||||
<span
|
||||
class="title-caret"
|
||||
id="public-channels-caret"
|
||||
role="button"
|
||||
title="toggle nav list"
|
||||
{{on "click" (fn this.toggleChannelSection "public-channels")}}
|
||||
data-toggleable="public-channels"
|
||||
>
|
||||
{{dIcon "angle-up"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
<span class="channel-title">{{i18n "chat.chat_channels"}}</span>
|
||||
|
||||
<LinkTo
|
||||
@route="chat.browse"
|
||||
class="btn no-text btn-flat open-browse-page-btn title-action"
|
||||
title={{i18n "chat.channels_list_popup.browse"}}
|
||||
>
|
||||
{{dIcon "pencil-alt"}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div
|
||||
id="public-channels"
|
||||
class={{concatClass
|
||||
"channels-list-container"
|
||||
"public-channels"
|
||||
(if this.inSidebar "collapsible-sidebar-section")
|
||||
}}
|
||||
>
|
||||
{{#if this.publicMessageChannelsEmpty}}
|
||||
<div class="channel-list-empty-message">
|
||||
<span class="channel-title">{{i18n
|
||||
"chat.no_public_channels"
|
||||
}}</span>
|
||||
<LinkTo @route="chat.browse">
|
||||
{{i18n "chat.click_to_join"}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
{{else}}
|
||||
{{#each
|
||||
this.chatChannelsManager.publicMessageChannels
|
||||
as |channel|
|
||||
}}
|
||||
<ChatChannelRow
|
||||
@channel={{channel}}
|
||||
@options={{hash settingsButton=true}}
|
||||
/>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<PluginOutlet
|
||||
@name="below-public-chat-channels"
|
||||
@tagName=""
|
||||
@outletArgs={{hash inSidebar=this.inSidebar}}
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -1,298 +1,16 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { fn, hash } from "@ember/helper";
|
||||
import { on } from "@ember/modifier";
|
||||
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 { inject as service } from "@ember/service";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import noop from "discourse/helpers/noop";
|
||||
import dIcon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import and from "truth-helpers/helpers/and";
|
||||
import not from "truth-helpers/helpers/not";
|
||||
import ChatModalNewMessage from "discourse/plugins/chat/discourse/components/chat/modal/new-message";
|
||||
import onResize from "../modifiers/chat/on-resize";
|
||||
import ChatChannelRow from "./chat-channel-row";
|
||||
import ChannelsListDirect from "discourse/plugins/chat/discourse/components/channels-list-direct";
|
||||
import ChannelsListPublic from "discourse/plugins/chat/discourse/components/channels-list-public";
|
||||
|
||||
export default class ChannelsList extends Component {
|
||||
@service chat;
|
||||
@service router;
|
||||
@service chatStateManager;
|
||||
@service chatChannelsManager;
|
||||
@service site;
|
||||
@service siteSettings;
|
||||
@service session;
|
||||
@service currentUser;
|
||||
@service modal;
|
||||
|
||||
@tracked hasScrollbar = false;
|
||||
|
||||
@action
|
||||
computeHasScrollbar(element) {
|
||||
this.hasScrollbar = element.scrollHeight > element.clientHeight;
|
||||
}
|
||||
|
||||
@action
|
||||
computeResizedEntries(entries) {
|
||||
this.computeHasScrollbar(entries[0].target);
|
||||
}
|
||||
|
||||
@action
|
||||
openNewMessageModal() {
|
||||
this.modal.show(ChatModalNewMessage);
|
||||
}
|
||||
|
||||
get showMobileDirectMessageButton() {
|
||||
return this.site.mobileView && this.canCreateDirectMessageChannel;
|
||||
}
|
||||
|
||||
get inSidebar() {
|
||||
return this.args.inSidebar ?? false;
|
||||
}
|
||||
|
||||
get publicMessageChannelsEmpty() {
|
||||
return this.chatChannelsManager.publicMessageChannels?.length === 0;
|
||||
}
|
||||
|
||||
get createDirectMessageChannelLabel() {
|
||||
if (!this.canCreateDirectMessageChannel) {
|
||||
return "chat.direct_messages.cannot_create";
|
||||
}
|
||||
|
||||
return "chat.direct_messages.new";
|
||||
}
|
||||
|
||||
get showDirectMessageChannels() {
|
||||
return (
|
||||
this.canCreateDirectMessageChannel ||
|
||||
this.chatChannelsManager.directMessageChannels?.length > 0
|
||||
);
|
||||
}
|
||||
|
||||
get canCreateDirectMessageChannel() {
|
||||
return this.chat.userCanDirectMessage;
|
||||
}
|
||||
|
||||
get displayPublicChannels() {
|
||||
if (!this.siteSettings.enable_public_channels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.publicMessageChannelsEmpty) {
|
||||
return (
|
||||
this.currentUser?.staff ||
|
||||
this.currentUser?.has_joinable_public_channels
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
get directMessageChannelClasses() {
|
||||
return `channels-list-container direct-message-channels ${
|
||||
this.inSidebar ? "collapsible-sidebar-section" : ""
|
||||
}`;
|
||||
}
|
||||
|
||||
get hasUnreadThreads() {
|
||||
return this.chatChannelsManager.publicMessageChannels.some(
|
||||
(channel) => channel.unreadThreadsCount > 0
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
toggleChannelSection(section) {
|
||||
this.args.toggleSection(section);
|
||||
}
|
||||
|
||||
didRender() {
|
||||
super.didRender(...arguments);
|
||||
|
||||
schedule("afterRender", this._applyScrollPosition);
|
||||
}
|
||||
|
||||
@action
|
||||
storeScrollPosition() {
|
||||
if (this.chatStateManager.isDrawerActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scrollTop = document.querySelector(".channels-list")?.scrollTop || 0;
|
||||
this.session.channelsListPosition = scrollTop;
|
||||
}
|
||||
|
||||
@bind
|
||||
_applyScrollPosition() {
|
||||
if (this.chatStateManager.isDrawerActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
const position = this.chatStateManager.isFullPageActive
|
||||
? this.session.channelsListPosition || 0
|
||||
: 0;
|
||||
const scroller = document.querySelector(".channels-list");
|
||||
scroller.scrollTo(0, position);
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.showMobileDirectMessageButton}}
|
||||
<DButton
|
||||
@icon="plus"
|
||||
class="no-text btn-flat open-new-message-btn keep-mobile-sidebar-open btn-floating"
|
||||
@action={{this.openNewMessageModal}}
|
||||
title={{i18n this.createDirectMessageChannelLabel}}
|
||||
/>
|
||||
<ChannelsListPublic />
|
||||
|
||||
{{#if this.chat.userCanAccessDirectMessages}}
|
||||
<ChannelsListDirect />
|
||||
{{/if}}
|
||||
|
||||
<div
|
||||
role="region"
|
||||
aria-label={{i18n "chat.aria_roles.channels_list"}}
|
||||
class={{concatClass
|
||||
"channels-list"
|
||||
(if this.hasScrollbar "has-scrollbar")
|
||||
}}
|
||||
{{on
|
||||
"scroll"
|
||||
(if
|
||||
this.chatStateManager.isFullPageActive this.storeScrollPosition (noop)
|
||||
)
|
||||
}}
|
||||
{{didInsert this.computeHasScrollbar}}
|
||||
{{onResize this.computeResizedEntries}}
|
||||
>
|
||||
|
||||
<LinkTo @route="chat.threads" class="chat-channel-row --threads">
|
||||
<span class="chat-channel-title">
|
||||
{{dIcon "discourse-threads" class="chat-user-threads__icon"}}
|
||||
{{i18n "chat.my_threads.title"}}
|
||||
</span>
|
||||
{{#if this.hasUnreadThreads}}
|
||||
<div class="c-unread-indicator">
|
||||
<div class="c-unread-indicator__number"> </div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
|
||||
{{#if this.displayPublicChannels}}
|
||||
<div class="chat-channel-divider public-channels-section">
|
||||
{{#if this.inSidebar}}
|
||||
<span
|
||||
class="title-caret"
|
||||
id="public-channels-caret"
|
||||
role="button"
|
||||
title="toggle nav list"
|
||||
{{on "click" (fn this.toggleChannelSection "public-channels")}}
|
||||
data-toggleable="public-channels"
|
||||
>
|
||||
{{dIcon "angle-up"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
<span class="channel-title">{{i18n "chat.chat_channels"}}</span>
|
||||
|
||||
<LinkTo
|
||||
@route="chat.browse"
|
||||
class="btn no-text btn-flat open-browse-page-btn title-action"
|
||||
title={{i18n "chat.channels_list_popup.browse"}}
|
||||
>
|
||||
{{dIcon "pencil-alt"}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="public-channels"
|
||||
class={{concatClass
|
||||
"channels-list-container"
|
||||
"public-channels"
|
||||
(if this.inSidebar "collapsible-sidebar-section")
|
||||
}}
|
||||
>
|
||||
{{#if this.publicChannelsEmpty}}
|
||||
<div class="public-channel-empty-message">
|
||||
<span class="channel-title">{{i18n
|
||||
"chat.no_public_channels"
|
||||
}}</span>
|
||||
<LinkTo @route="chat.browse">
|
||||
{{i18n "chat.click_to_join"}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
{{else}}
|
||||
{{#each
|
||||
this.chatChannelsManager.publicMessageChannels
|
||||
as |channel|
|
||||
}}
|
||||
<ChatChannelRow
|
||||
@channel={{channel}}
|
||||
@options={{hash settingsButton=true}}
|
||||
/>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<PluginOutlet
|
||||
@name="below-public-chat-channels"
|
||||
@tagName=""
|
||||
@outletArgs={{hash inSidebar=this.inSidebar}}
|
||||
/>
|
||||
|
||||
{{#if this.showDirectMessageChannels}}
|
||||
<div class="chat-channel-divider direct-message-channels-section">
|
||||
{{#if this.inSidebar}}
|
||||
<span
|
||||
class="title-caret"
|
||||
id="direct-message-channels-caret"
|
||||
role="button"
|
||||
title="toggle nav list"
|
||||
{{on
|
||||
"click"
|
||||
(fn this.toggleChannelSection "direct-message-channels")
|
||||
}}
|
||||
data-toggleable="direct-message-channels"
|
||||
>
|
||||
{{dIcon "angle-up"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
<span class="channel-title">{{i18n
|
||||
"chat.direct_messages.title"
|
||||
}}</span>
|
||||
|
||||
{{#if
|
||||
(and
|
||||
this.canCreateDirectMessageChannel
|
||||
(not this.showMobileDirectMessageButton)
|
||||
)
|
||||
}}
|
||||
<DButton
|
||||
@icon="plus"
|
||||
class="no-text btn-flat open-new-message-btn"
|
||||
@action={{this.openNewMessageModal}}
|
||||
title={{i18n this.createDirectMessageChannelLabel}}
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div
|
||||
id="direct-message-channels"
|
||||
class={{this.directMessageChannelClasses}}
|
||||
>
|
||||
{{#each
|
||||
this.chatChannelsManager.truncatedDirectMessageChannels
|
||||
as |channel|
|
||||
}}
|
||||
<ChatChannelRow
|
||||
@channel={{channel}}
|
||||
@options={{hash leaveButton=true}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { modifier } from "ember-modifier";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import eq from "truth-helpers/helpers/eq";
|
||||
|
||||
export default class ChatFooter extends Component {
|
||||
@service router;
|
||||
@service chat;
|
||||
@service chatApi;
|
||||
|
||||
@tracked threadsEnabled = false;
|
||||
|
||||
updateThreadCount = modifier(() => {
|
||||
const ajax = this.chatApi.userThreadCount();
|
||||
|
||||
ajax
|
||||
.then((result) => {
|
||||
this.threadsEnabled = result.thread_count > 0;
|
||||
})
|
||||
.catch((error) => {
|
||||
popupAjaxError(error);
|
||||
});
|
||||
|
||||
return () => {
|
||||
ajax?.abort();
|
||||
};
|
||||
});
|
||||
|
||||
get directMessagesEnabled() {
|
||||
return this.chat.userCanAccessDirectMessages;
|
||||
}
|
||||
|
||||
get shouldRenderFooter() {
|
||||
return this.directMessagesEnabled || this.threadsEnabled;
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.shouldRenderFooter}}
|
||||
<nav class="c-footer" {{this.updateThreadCount}}>
|
||||
{{#if this.directMessagesEnabled}}
|
||||
<DButton
|
||||
@route="chat.direct-messages"
|
||||
@class={{concatClass
|
||||
"btn-flat"
|
||||
"c-footer__item"
|
||||
(if
|
||||
(eq this.router.currentRouteName "chat.direct-messages")
|
||||
"--active"
|
||||
)
|
||||
}}
|
||||
@icon="users"
|
||||
@id="c-footer-direct-messages"
|
||||
@translatedLabel={{i18n "chat.direct_messages.title"}}
|
||||
aria-label={{i18n "chat.direct_messages.aria_label"}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
<DButton
|
||||
@route="chat.channels"
|
||||
@class={{concatClass
|
||||
"btn-flat"
|
||||
"c-footer__item"
|
||||
(if (eq this.router.currentRouteName "chat.channels") "--active")
|
||||
}}
|
||||
@icon="comments"
|
||||
@id="c-footer-channels"
|
||||
@translatedLabel={{i18n "chat.channel_list.title"}}
|
||||
aria-label={{i18n "chat.channel_list.aria_label"}}
|
||||
/>
|
||||
|
||||
{{#if this.threadsEnabled}}
|
||||
<DButton
|
||||
@route="chat.threads"
|
||||
@class={{concatClass
|
||||
"btn-flat"
|
||||
"c-footer__item"
|
||||
(if (eq this.router.currentRouteName "chat.threads") "--active")
|
||||
}}
|
||||
@icon="discourse-threads"
|
||||
@id="c-footer-threads"
|
||||
@translatedLabel={{i18n "chat.my_threads.title"}}
|
||||
aria-label={{i18n "chat.my_threads.aria_label"}}
|
||||
/>
|
||||
{{/if}}
|
||||
</nav>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
import { hash } from "@ember/helper";
|
||||
import BrowseChannelsButton from "./browse-channels-button";
|
||||
import CloseDrawerButton from "./close-drawer-button";
|
||||
import CloseThreadButton from "./close-thread-button";
|
||||
import CloseThreadsButton from "./close-threads-button";
|
||||
import FullPageButton from "./full-page-button";
|
||||
import NewChannelButton from "./new-channel-button";
|
||||
import NewDirectMessageButton from "./new-direct-message-button";
|
||||
import OpenDrawerButton from "./open-drawer-button";
|
||||
import ThreadSettingsButton from "./thread-settings-button";
|
||||
import ThreadTrackingDropdown from "./thread-tracking-dropdown";
|
||||
|
@ -15,6 +17,8 @@ const ChatNavbarActions = <template>
|
|||
{{yield
|
||||
(hash
|
||||
OpenDrawerButton=OpenDrawerButton
|
||||
BrowseChannelsButton=BrowseChannelsButton
|
||||
NewDirectMessageButton=NewDirectMessageButton
|
||||
NewChannelButton=NewChannelButton
|
||||
ThreadTrackingDropdown=ThreadTrackingDropdown
|
||||
CloseThreadButton=CloseThreadButton
|
||||
|
|
|
@ -2,6 +2,7 @@ import Component from "@glimmer/component";
|
|||
import { LinkTo } from "@ember/routing";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import I18n from "I18n";
|
||||
import { FOOTER_NAV_ROUTES } from "discourse/plugins/chat/discourse/lib/chat-constants";
|
||||
|
||||
export default class ChatNavbarBackButton extends Component {
|
||||
get icon() {
|
||||
|
@ -12,6 +13,14 @@ export default class ChatNavbarBackButton extends Component {
|
|||
return this.args.title ?? I18n.t("chat.browse.back");
|
||||
}
|
||||
|
||||
get targetRoute() {
|
||||
if (FOOTER_NAV_ROUTES.includes(this.args.route)) {
|
||||
return this.args.route;
|
||||
} else {
|
||||
return "chat";
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if @routeModels}}
|
||||
<LinkTo
|
||||
|
@ -28,7 +37,7 @@ export default class ChatNavbarBackButton extends Component {
|
|||
</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo
|
||||
@route="chat"
|
||||
@route={{this.targetRoute}}
|
||||
class="c-navbar__back-button no-text btn-transparent btn"
|
||||
title={{this.title}}
|
||||
>
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import { inject as service } from "@ember/service";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default class ChatNavbarBrowseChannelsButton extends Component {
|
||||
@service router;
|
||||
|
||||
browseChannelsLabel = I18n.t("chat.channels_list_popup.browse");
|
||||
|
||||
get showBrowseChannelsButton() {
|
||||
return this.router.currentRoute.name === "chat.channels";
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.showBrowseChannelsButton}}
|
||||
<LinkTo
|
||||
@route="chat.browse"
|
||||
class="btn no-text btn-flat c-navbar__browse-button"
|
||||
title={{this.browseChannelsLabel}}
|
||||
>
|
||||
{{icon "pencil-alt"}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import I18n from "I18n";
|
||||
import ChatModalNewMessage from "discourse/plugins/chat/discourse/components/chat/modal/new-message";
|
||||
|
||||
export default class ChatNavbarNewDirectMessageButton extends Component {
|
||||
@service router;
|
||||
@service modal;
|
||||
@service chat;
|
||||
|
||||
buttonLabel = I18n.t("chat.channels_list_popup.browse");
|
||||
|
||||
get showButtonComponent() {
|
||||
return (
|
||||
this.router.currentRoute.name === "chat.direct-messages" &&
|
||||
this.canCreateDirectMessageChannel
|
||||
);
|
||||
}
|
||||
|
||||
get canCreateDirectMessageChannel() {
|
||||
return this.chat.userCanDirectMessage;
|
||||
}
|
||||
|
||||
@action
|
||||
openNewMessageModal() {
|
||||
this.modal.show(ChatModalNewMessage);
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.showButtonComponent}}
|
||||
<DButton
|
||||
class="btn no-text btn-flat c-navbar__new-dm-button"
|
||||
title={{this.buttonLabel}}
|
||||
@action={{this.openNewMessageModal}}
|
||||
@icon="plus"
|
||||
/>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -7,11 +7,17 @@ import FullPageChat from "discourse/plugins/chat/discourse/components/full-page-
|
|||
export default class ChatRoutesChannel extends Component {
|
||||
@service site;
|
||||
|
||||
get getChannelsRoute() {
|
||||
return this.args.channel.isDirectMessageChannel
|
||||
? "chat.direct-messages"
|
||||
: "chat.channels";
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="c-routes-channel">
|
||||
<Navbar as |navbar|>
|
||||
{{#if this.site.mobileView}}
|
||||
<navbar.BackButton />
|
||||
<navbar.BackButton @route={{this.getChannelsRoute}} />
|
||||
{{/if}}
|
||||
<navbar.ChannelTitle @channel={{@channel}} />
|
||||
<navbar.Actions as |action|>
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { inject as service } from "@ember/service";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import ChannelsListPublic from "discourse/plugins/chat/discourse/components/channels-list-public";
|
||||
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
|
||||
|
||||
export default class ChatRoutesChannels extends Component {
|
||||
@service site;
|
||||
|
||||
<template>
|
||||
<div class="c-routes-channels">
|
||||
<Navbar as |navbar|>
|
||||
<navbar.Title @title={{i18n "chat.chat_channels"}} />
|
||||
<navbar.Actions as |action|>
|
||||
<action.BrowseChannelsButton />
|
||||
</navbar.Actions>
|
||||
</Navbar>
|
||||
|
||||
<ChannelsListPublic />
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { inject as service } from "@ember/service";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import ChannelsListDirect from "discourse/plugins/chat/discourse/components/channels-list-direct";
|
||||
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
|
||||
|
||||
export default class ChatRoutesDirectMessages extends Component {
|
||||
@service site;
|
||||
|
||||
<template>
|
||||
<div class="c-routes-direct-messages">
|
||||
<Navbar as |navbar|>
|
||||
<navbar.Title @title={{i18n "chat.direct_messages.title"}} />
|
||||
<navbar.Actions as |action|>
|
||||
<action.NewDirectMessageButton />
|
||||
</navbar.Actions>
|
||||
</Navbar>
|
||||
|
||||
<ChannelsListDirect />
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -10,13 +10,7 @@ export default class ChatRoutesThreads extends Component {
|
|||
<template>
|
||||
<div class="c-routes-threads">
|
||||
<Navbar as |navbar|>
|
||||
{{#if this.site.mobileView}}
|
||||
<navbar.BackButton />
|
||||
{{/if}}
|
||||
<navbar.Title
|
||||
@title={{i18n "chat.my_threads.title"}}
|
||||
@icon="discourse-threads"
|
||||
/>
|
||||
<navbar.Title @title={{i18n "chat.my_threads.title"}} />
|
||||
|
||||
<navbar.Actions as |action|>
|
||||
<action.OpenDrawerButton />
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Controller from "@ember/controller";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { FOOTER_NAV_ROUTES } from "discourse/plugins/chat/discourse/lib/chat-constants";
|
||||
|
||||
export default class ChatController extends Controller {
|
||||
@service chat;
|
||||
|
@ -22,6 +23,13 @@ export default class ChatController extends Controller {
|
|||
return this.siteSettings.navigation_menu === "sidebar";
|
||||
}
|
||||
|
||||
get shouldUseChatFooter() {
|
||||
return (
|
||||
this.site.mobileView &&
|
||||
FOOTER_NAV_ROUTES.includes(this.router.currentRouteName)
|
||||
);
|
||||
}
|
||||
|
||||
get mainOutletModifierClasses() {
|
||||
let modifierClasses = [];
|
||||
|
||||
|
|
|
@ -2,3 +2,8 @@ export const PAST = "past";
|
|||
export const FUTURE = "future";
|
||||
export const READ_INTERVAL_MS = 1000;
|
||||
export const DEFAULT_MESSAGE_PAGE_SIZE = 50;
|
||||
export const FOOTER_NAV_ROUTES = [
|
||||
"chat.direct-messages",
|
||||
"chat.channels",
|
||||
"chat.threads",
|
||||
];
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import { inject as service } from "@ember/service";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default class ChatChannelsRoute extends DiscourseRoute {
|
||||
@service chat;
|
||||
@service chatChannelsManager;
|
||||
|
||||
activate() {
|
||||
this.chat.activeChannel = null;
|
||||
}
|
||||
|
||||
model() {
|
||||
return this.chatChannelsManager.publicMessageChannels;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import { inject as service } from "@ember/service";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default class ChatDirectMessagesRoute extends DiscourseRoute {
|
||||
@service chat;
|
||||
@service chatChannelsManager;
|
||||
|
||||
activate() {
|
||||
this.chat.activeChannel = null;
|
||||
}
|
||||
|
||||
model() {
|
||||
return this.chatChannelsManager.directMessageChannels;
|
||||
}
|
||||
}
|
|
@ -11,9 +11,13 @@ export default class ChatIndexRoute extends DiscourseRoute {
|
|||
}
|
||||
|
||||
redirect() {
|
||||
// Always want the channel index on mobile.
|
||||
// on mobile redirect user to the first footer tab route
|
||||
if (this.site.mobileView) {
|
||||
return;
|
||||
if (this.chat.userCanAccessDirectMessages) {
|
||||
return this.router.replaceWith("chat.direct-messages");
|
||||
} else {
|
||||
return this.router.replaceWith("chat.channels");
|
||||
}
|
||||
}
|
||||
|
||||
// We are on desktop. Check for a channel to enter and transition if so
|
||||
|
@ -26,10 +30,4 @@ export default class ChatIndexRoute extends DiscourseRoute {
|
|||
return this.router.replaceWith("chat.browse");
|
||||
}
|
||||
}
|
||||
|
||||
model() {
|
||||
if (this.site.mobileView) {
|
||||
return this.chatChannelsManager.channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ export default class ChatRoute extends DiscourseRoute {
|
|||
|
||||
const INTERCEPTABLE_ROUTES = [
|
||||
"chat.channel",
|
||||
"chat.direct-messages",
|
||||
"chat.channels",
|
||||
"chat.threads",
|
||||
"chat.channel.thread",
|
||||
"chat.channel.thread.index",
|
||||
|
|
|
@ -321,6 +321,13 @@ export default class ChatApi extends Service {
|
|||
return new Collection(`${this.#basePath}/me/threads`, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total number of threads for the current user.
|
||||
*/
|
||||
userThreadCount() {
|
||||
return this.#getRequest("/me/threads/count");
|
||||
}
|
||||
|
||||
/**
|
||||
* Update notifications settings of current user for a channel.
|
||||
* @param {number} channelId - The ID of the channel.
|
||||
|
|
|
@ -74,6 +74,15 @@ export default class Chat extends Service {
|
|||
);
|
||||
}
|
||||
|
||||
@computed("chatChannelsManager.directMessageChannels")
|
||||
get userHasDirectMessages() {
|
||||
return this.chatChannelsManager.directMessageChannels?.length > 0;
|
||||
}
|
||||
|
||||
get userCanAccessDirectMessages() {
|
||||
return this.userCanDirectMessage || this.userHasDirectMessages;
|
||||
}
|
||||
|
||||
@computed("activeChannel.userSilenced")
|
||||
get userCanInteractWithChat() {
|
||||
return !this.activeChannel?.userSilenced;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<Chat::Routes::Channels />
|
|
@ -0,0 +1 @@
|
|||
<Chat::Routes::DirectMessages />
|
|
@ -22,6 +22,9 @@
|
|||
class={{concat-class "main-chat-outlet" this.mainOutletModifierClasses}}
|
||||
>
|
||||
{{outlet}}
|
||||
{{#if this.shouldUseChatFooter}}
|
||||
<ChatFooter />
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
|
@ -1,11 +1,11 @@
|
|||
.btn-floating.open-new-message-btn {
|
||||
position: fixed;
|
||||
background: var(--tertiary);
|
||||
bottom: 2rem;
|
||||
bottom: 5rem;
|
||||
right: 2rem;
|
||||
border-radius: 50%;
|
||||
font-size: var(--font-up-4);
|
||||
padding: 1rem;
|
||||
font-size: var(--font-up-3);
|
||||
padding: 0.5rem;
|
||||
transition: transform 0.25s ease, box-shadow 0.25s ease;
|
||||
z-index: z("usercard");
|
||||
box-shadow: 0px 5px 5px -1px rgba(0, 0, 0, 0.25);
|
||||
|
@ -27,9 +27,6 @@
|
|||
}
|
||||
|
||||
.channels-list {
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
height: 100%;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
position: relative;
|
||||
@include chat-scrollbar();
|
||||
|
@ -65,8 +62,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
.public-channel-empty-message {
|
||||
margin: 0 0.5em 0.5em 0.5em;
|
||||
.channel-list-empty-message {
|
||||
margin: 1em 0.5em 0.5em 0.5em;
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,10 +32,6 @@
|
|||
@include ellipsis();
|
||||
font-weight: 700;
|
||||
|
||||
.c-routes-channel-threads & {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.chat-drawer & {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
}
|
||||
|
||||
.chat-side-panel {
|
||||
grid-area: threads;
|
||||
box-sizing: border-box;
|
||||
border-left: 1px solid var(--primary-low);
|
||||
position: relative;
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.public-channel-empty-message {
|
||||
.channel-list-empty-message {
|
||||
margin: 0;
|
||||
padding: 0em 2em 0.5em;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ html.has-full-page-chat {
|
|||
|
||||
&.has-side-panel-expanded {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas: "threads";
|
||||
|
||||
.c-routes-channel {
|
||||
display: none;
|
||||
|
@ -40,8 +39,6 @@ html.has-full-page-chat {
|
|||
|
||||
.btn:active,
|
||||
.btn:hover {
|
||||
background: var(--secondary-very-high);
|
||||
|
||||
.d-icon {
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
.full-page-chat {
|
||||
#main-chat-outlet.chat-view {
|
||||
grid-template-areas:
|
||||
"list"
|
||||
"footer";
|
||||
}
|
||||
|
||||
.channels-list {
|
||||
grid-area: list;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.c-footer {
|
||||
grid-area: footer;
|
||||
background: var(--secondary);
|
||||
border-top: 1px solid var(--primary-low);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-around;
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex-basis: 33%;
|
||||
flex-shrink: 0;
|
||||
padding-block: 0.75rem;
|
||||
height: 100%;
|
||||
|
||||
&.--active {
|
||||
.d-icon,
|
||||
.d-button-label {
|
||||
color: var(--quaternary);
|
||||
}
|
||||
}
|
||||
|
||||
.d-icon {
|
||||
margin-right: 0;
|
||||
font-size: var(--font-up-4);
|
||||
color: var(--primary-medium);
|
||||
|
||||
&.d-icon-discourse-threads {
|
||||
font-size: var(--font-up-3); //visual correction
|
||||
}
|
||||
}
|
||||
|
||||
.d-button-label {
|
||||
font-size: var(--font-down-1-rem);
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
.full-page-chat {
|
||||
.channels-list {
|
||||
overflow-y: overlay;
|
||||
padding-bottom: 6rem;
|
||||
box-sizing: border-box;
|
||||
|
||||
|
@ -19,7 +18,7 @@
|
|||
}
|
||||
|
||||
.channel-title {
|
||||
color: var(--quaternary);
|
||||
color: var(--primary);
|
||||
font-size: var(--font-down-1);
|
||||
}
|
||||
}
|
||||
|
@ -73,3 +72,10 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-routes-direct-messages,
|
||||
.c-routes-channels,
|
||||
.c-routes-threads {
|
||||
background: var(--primary-very-low);
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
.chat-message-thread-indicator {
|
||||
.c-routes-threads & {
|
||||
background: var(--secondary);
|
||||
}
|
||||
grid-template-areas:
|
||||
"avatar info info participants"
|
||||
"excerpt excerpt excerpt replies";
|
||||
|
|
|
@ -4,8 +4,18 @@
|
|||
max-width: 100vw;
|
||||
padding-inline: 0.25rem;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
&__back-button {
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.c-navbar__title {
|
||||
.c-routes-direct-messages &,
|
||||
.c-routes-channels &,
|
||||
.c-routes-threads & {
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,3 +3,7 @@
|
|||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.c-user-thread {
|
||||
margin-inline: 1.5rem;
|
||||
}
|
||||
|
|
|
@ -22,3 +22,4 @@
|
|||
@import "chat-thread-list-header";
|
||||
@import "chat-user-threads";
|
||||
@import "chat-header";
|
||||
@import "chat-footer";
|
||||
|
|
|
@ -226,6 +226,7 @@ en:
|
|||
composer: "Chat composer"
|
||||
channels_list: "Chat channels list"
|
||||
|
||||
no_direct_message_channels: "You have not joined any direct message channels."
|
||||
no_public_channels: "You have not joined any channels."
|
||||
kicked_from_channel: "You can no longer access this channel."
|
||||
only_chat_push_notifications:
|
||||
|
@ -497,16 +498,22 @@ en:
|
|||
export_has_started: "The export has started. You'll receive a PM when it's ready."
|
||||
|
||||
my_threads:
|
||||
title: My threads
|
||||
title: My Threads
|
||||
aria_label: "Switch to my threads list"
|
||||
|
||||
direct_messages:
|
||||
title: "Personal chat"
|
||||
title: "DMs"
|
||||
aria_label: "Switch to direct messages"
|
||||
new: "Create a personal chat"
|
||||
create: "Go"
|
||||
leave: "Leave this personal chat"
|
||||
close: "Close this personal chat"
|
||||
cannot_create: "Sorry, you cannot send direct messages."
|
||||
|
||||
channel_list:
|
||||
title: "Channels"
|
||||
aria_label: "Switch to channels list"
|
||||
|
||||
incoming_webhooks:
|
||||
back: "Back"
|
||||
channel_placeholder: "Select a channel"
|
||||
|
|
|
@ -6,6 +6,7 @@ Chat::Engine.routes.draw do
|
|||
get "/channels" => "channels#index"
|
||||
get "/me/channels" => "current_user_channels#index"
|
||||
get "/me/threads" => "current_user_threads#index"
|
||||
get "/me/threads/count" => "current_user_threads#thread_count"
|
||||
post "/channels" => "channels#create"
|
||||
put "/channels/read/" => "reads#update_all"
|
||||
put "/channels/:channel_id/read/:message_id" => "reads#update"
|
||||
|
@ -73,6 +74,8 @@ Chat::Engine.routes.draw do
|
|||
|
||||
# chat_controller routes
|
||||
get "/" => "chat#respond"
|
||||
get "/direct-messages" => "chat#respond"
|
||||
get "/channels" => "chat#respond"
|
||||
get "/threads" => "chat#respond"
|
||||
get "/browse" => "chat#respond"
|
||||
get "/browse/all" => "chat#respond"
|
||||
|
|
|
@ -29,4 +29,24 @@ describe Chat::Api::CurrentUserThreadsController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#thread_count" do
|
||||
describe "success" do
|
||||
it "works" do
|
||||
get "/chat/api/me/threads/count"
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body["thread_count"]).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context "when threads are not found" do
|
||||
it "returns a 200 when there are no threads" do
|
||||
get "/chat/api/me/threads/count"
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body["thread_count"]).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -69,7 +69,7 @@ RSpec.describe "Browse page", type: :system do
|
|||
chat_page.visit_browse
|
||||
find(".c-navbar__back-button").click
|
||||
|
||||
expect(browse_page).to have_current_path("/chat")
|
||||
expect(browse_page).to have_current_path("/chat/direct-messages")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Chat footer on mobile", type: :system, mobile: true do
|
||||
fab!(:user)
|
||||
fab!(:channel) { Fabricate(:chat_channel, threading_enabled: true) }
|
||||
fab!(:message) { Fabricate(:chat_message, chat_channel: channel, user: user) }
|
||||
let(:chat_page) { PageObjects::Pages::Chat.new }
|
||||
|
||||
before do
|
||||
chat_system_bootstrap
|
||||
sign_in(user)
|
||||
channel.add(user)
|
||||
end
|
||||
|
||||
context "with multiple tabs" do
|
||||
it "shows footer" do
|
||||
visit("/")
|
||||
chat_page.open_from_header
|
||||
|
||||
expect(page).to have_css(".c-footer")
|
||||
expect(page).to have_css(".c-footer__item", count: 2)
|
||||
expect(page).to have_css("#c-footer-direct-messages")
|
||||
expect(page).to have_css("#c-footer-channels")
|
||||
end
|
||||
|
||||
it "hides footer when channel is open" do
|
||||
chat_page.visit_channel(channel)
|
||||
|
||||
expect(page).to have_no_css(".c-footer")
|
||||
end
|
||||
|
||||
it "redirects the user to the direct messages tab" do
|
||||
visit("/")
|
||||
chat_page.open_from_header
|
||||
|
||||
expect(page).to have_current_path("/chat/direct-messages")
|
||||
end
|
||||
|
||||
it "shows threads tab when user has threads" do
|
||||
thread = Fabricate(:chat_thread, channel: channel, original_message: message)
|
||||
Fabricate(:chat_message, chat_channel: channel, thread: thread)
|
||||
thread.update!(replies_count: 1)
|
||||
|
||||
visit("/")
|
||||
chat_page.open_from_header
|
||||
|
||||
expect(page).to have_css(".c-footer")
|
||||
expect(page).to have_css("#c-footer-threads")
|
||||
end
|
||||
end
|
||||
|
||||
context "with only 1 tab" do
|
||||
before do
|
||||
SiteSetting.direct_message_enabled_groups = "3" # staff only
|
||||
end
|
||||
|
||||
it "does not render footer" do
|
||||
visit("/")
|
||||
chat_page.open_from_header
|
||||
|
||||
expect(page).to have_no_css(".c-footer")
|
||||
end
|
||||
|
||||
it "redirects user to channels page" do
|
||||
visit("/")
|
||||
chat_page.open_from_header
|
||||
|
||||
expect(page).to have_current_path("/chat/channels")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -19,14 +19,14 @@ RSpec.describe "List channels | mobile", type: :system, mobile: true do
|
|||
before { category_channel_1.add(current_user) }
|
||||
|
||||
it "shows the channel in the correct section" do
|
||||
visit("/chat")
|
||||
visit("/chat/channels")
|
||||
expect(page.find(".public-channels")).to have_content(category_channel_1.name)
|
||||
end
|
||||
end
|
||||
|
||||
context "when not member of the channel" do
|
||||
it "doesn’t show the channel" do
|
||||
visit("/chat")
|
||||
visit("/chat/channels")
|
||||
|
||||
expect(page.find(".public-channels", visible: :all)).to have_no_content(
|
||||
category_channel_1.name,
|
||||
|
@ -46,6 +46,7 @@ RSpec.describe "List channels | mobile", type: :system, mobile: true do
|
|||
|
||||
it "sorts them alphabetically" do
|
||||
visit("/chat")
|
||||
page.find("#c-footer-channels").click
|
||||
|
||||
expect(page.find("#public-channels a:nth-child(1)")["data-chat-channel-id"]).to eq(
|
||||
channel_2.id.to_s,
|
||||
|
@ -77,25 +78,25 @@ RSpec.describe "List channels | mobile", type: :system, mobile: true do
|
|||
end
|
||||
|
||||
context "when no category channels" do
|
||||
it "doesn’t show the section" do
|
||||
visit("/chat")
|
||||
expect(page).to have_no_css(".public-channels-section")
|
||||
it "hides the section" do
|
||||
visit("/chat/channels")
|
||||
expect(page).to have_no_css(".channels-list-container")
|
||||
end
|
||||
|
||||
context "when user can create channels" do
|
||||
before { current_user.update!(admin: true) }
|
||||
|
||||
it "shows the section" do
|
||||
visit("/chat")
|
||||
expect(page).to have_css(".public-channels-section")
|
||||
visit("/chat/channels")
|
||||
expect(page).to have_css(".channels-list-container")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when no direct message channels" do
|
||||
it "shows the section" do
|
||||
visit("/chat")
|
||||
expect(page).to have_css(".direct-message-channels-section")
|
||||
visit("/chat/direct-messages")
|
||||
expect(page).to have_selector(".channels-list-container")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -123,8 +124,8 @@ RSpec.describe "List channels | mobile", type: :system, mobile: true do
|
|||
end
|
||||
|
||||
it "has a new dm channel button" do
|
||||
visit("/chat")
|
||||
find(".open-new-message-btn").click
|
||||
visit("/chat/direct-messages")
|
||||
find(".c-navbar__new-dm-button").click
|
||||
|
||||
expect(chat.message_creator).to be_opened
|
||||
end
|
||||
|
|
|
@ -80,7 +80,7 @@ RSpec.describe "Message notifications - mobile", type: :system, mobile: true do
|
|||
|
||||
context "when a message is created" do
|
||||
it "correctly renders notifications" do
|
||||
visit("/chat")
|
||||
visit("/chat/channels")
|
||||
|
||||
create_message(channel_1, user: user_1)
|
||||
|
||||
|
@ -93,7 +93,7 @@ RSpec.describe "Message notifications - mobile", type: :system, mobile: true do
|
|||
it "correctly renders notifications" do
|
||||
Jobs.run_immediately!
|
||||
|
||||
visit("/chat")
|
||||
visit("/chat/channels")
|
||||
|
||||
create_message(
|
||||
channel_1,
|
||||
|
@ -173,13 +173,15 @@ RSpec.describe "Message notifications - mobile", type: :system, mobile: true do
|
|||
|
||||
context "when messages are created" do
|
||||
it "correctly renders notifications" do
|
||||
visit("/chat")
|
||||
visit("/chat/channels")
|
||||
|
||||
create_message(channel_1, user: user_1)
|
||||
|
||||
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator", text: "")
|
||||
expect(channel_index_page).to have_unread_channel(channel_1)
|
||||
|
||||
visit("/chat/direct-messages")
|
||||
|
||||
create_message(dm_channel_1, user: user_1)
|
||||
|
||||
expect(channel_index_page).to have_unread_channel(dm_channel_1)
|
||||
|
|
|
@ -38,11 +38,11 @@ RSpec.describe "Navigation", type: :system do
|
|||
end
|
||||
|
||||
context "when clicking chat icon on mobile and is viewing channel" do
|
||||
it "navigates to index", mobile: true do
|
||||
it "navigates to direct messages tab", mobile: true do
|
||||
chat_page.visit_channel(category_channel_2)
|
||||
chat_page.open_from_header
|
||||
|
||||
expect(page).to have_current_path(chat_path)
|
||||
expect(page).to have_current_path("/chat/direct-messages")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ module PageObjects
|
|||
class ChannelIndex < PageObjects::Components::Base
|
||||
attr_reader :context
|
||||
|
||||
SELECTOR = ".channels-list"
|
||||
SELECTOR = ".channels-list:first-child"
|
||||
|
||||
def initialize(context = nil)
|
||||
@context = context
|
||||
|
|
Loading…
Reference in New Issue