UX: add illustrations for empty chat list + split into tabs on drawer (#26910)

Adds a placeholder image + CTA in chat, for empty channel and DM lists.

On desktop with drawer mode, we split chat into tabs (like mobile).

---------

Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
Co-authored-by: David Battersby <info@davidbattersby.com>
Co-authored-by: Régis Hanol <regis@hanol.fr>
This commit is contained in:
chapoi 2024-05-28 15:00:04 +02:00 committed by GitHub
parent e02b8b4a83
commit d0427919f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 363 additions and 213 deletions

View File

@ -3,11 +3,13 @@ import { fn, hash } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { service } from "@ember/service";
import { and } from "truth-helpers";
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 EmptyChannelsList from "discourse/plugins/chat/discourse/components/empty-channels-list";
import ChatChannelRow from "./chat-channel-row";
export default class ChannelsListDirect extends Component {
@ -16,11 +18,6 @@ export default class ChannelsListDirect extends Component {
@service site;
@service modal;
@action
openNewMessageModal() {
this.modal.show(ChatModalNewMessage);
}
get inSidebar() {
return this.args.inSidebar ?? false;
}
@ -58,6 +55,11 @@ export default class ChannelsListDirect extends Component {
this.args.toggleSection(section);
}
@action
openNewMessageModal() {
this.modal.show(ChatModalNewMessage);
}
<template>
<PluginOutlet
@name="below-direct-chat-channels"
@ -65,39 +67,35 @@ export default class ChannelsListDirect extends Component {
@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}}
{{#if (and this.showDirectMessageChannels 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>
<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 this.canCreateDirectMessageChannel}}
<DButton
@icon="plus"
class="no-text btn-flat open-new-message-btn"
@action={{this.openNewMessageModal}}
title={{i18n this.createDirectMessageChannelLabel}}
/>
{{/if}}
</div>
{{/if}}
<div
@ -105,11 +103,12 @@ export default class ChannelsListDirect extends Component {
class={{this.directMessageChannelClasses}}
>
{{#if this.directMessageChannelsEmpty}}
<div class="channel-list-empty-message">
<span class="channel-title">{{i18n
"chat.no_direct_message_channels"
}}</span>
</div>
<EmptyChannelsList
@title={{i18n "chat.no_direct_message_channels"}}
@ctaTitle={{i18n "chat.no_direct_message_channels_cta"}}
@ctaAction={{this.openNewMessageModal}}
@showCTA={{this.canCreateDirectMessageChannel}}
/>
{{else}}
{{#each
this.chatChannelsManager.truncatedDirectMessageChannels

View File

@ -9,6 +9,7 @@ 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 EmptyChannelsList from "discourse/plugins/chat/discourse/components/empty-channels-list";
import ChatChannelRow from "./chat-channel-row";
export default class ChannelsListPublic extends Component {
@ -18,6 +19,7 @@ export default class ChannelsListPublic extends Component {
@service site;
@service siteSettings;
@service currentUser;
@service router;
get inSidebar() {
return this.args.inSidebar ?? false;
@ -62,8 +64,13 @@ export default class ChannelsListPublic extends Component {
this.args.toggleSection(section);
}
@action
openBrowseChannels() {
this.router.transitionTo("chat.browse");
}
<template>
{{#if (and this.site.desktopView this.hasThreadedChannels)}}
{{#if (and this.site.desktopView this.inSidebar this.hasThreadedChannels)}}
<LinkTo @route="chat.threads" class="chat-channel-row --threads">
<span class="chat-channel-title">
{{dIcon "discourse-threads" class="chat-user-threads__icon"}}
@ -77,63 +84,58 @@ export default class ChannelsListPublic extends Component {
</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"}}
{{#if (and this.displayPublicChannels 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 "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}}
{{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}}
<EmptyChannelsList
@title={{i18n "chat.no_public_channels"}}
@ctaTitle={{i18n "chat.no_public_channels_cta"}}
@ctaAction={{this.openBrowseChannels}}
@showCTA={{this.displayPublicChannels}}
/>
{{else}}
{{#each this.chatChannelsManager.publicMessageChannels as |channel|}}
<ChatChannelRow
@channel={{channel}}
@options={{hash settingsButton=true}}
/>
{{/each}}
{{/if}}
</div>
<PluginOutlet
@name="below-public-chat-channels"
@tagName=""

View File

@ -1,4 +1,5 @@
import Component from "@glimmer/component";
import { fn } from "@ember/helper";
import { service } from "@ember/service";
import { eq } from "truth-helpers";
import DButton from "discourse/components/d-button";
@ -40,7 +41,7 @@ export default class ChatFooter extends Component {
{{#if this.shouldRenderFooter}}
<nav class="c-footer">
<DButton
@route="chat.channels"
@action={{fn @onClickTab "channels"}}
@icon="comments"
@translatedLabel={{i18n "chat.channel_list.title"}}
aria-label={{i18n "chat.channel_list.aria_label"}}
@ -48,7 +49,7 @@ export default class ChatFooter extends Component {
class={{concatClass
"btn-flat"
"c-footer__item"
(if (eq this.router.currentRouteName "chat.channels") "--active")
(if (eq @activeTab "channels") "--active")
}}
>
<UnreadChannelsIndicator />
@ -56,7 +57,7 @@ export default class ChatFooter extends Component {
{{#if this.directMessagesEnabled}}
<DButton
@route="chat.direct-messages"
@action={{fn @onClickTab "direct-messages"}}
@icon="users"
@translatedLabel={{i18n "chat.direct_messages.title"}}
aria-label={{i18n "chat.direct_messages.aria_label"}}
@ -64,10 +65,7 @@ export default class ChatFooter extends Component {
class={{concatClass
"btn-flat"
"c-footer__item"
(if
(eq this.router.currentRouteName "chat.direct-messages")
"--active"
)
(if (eq @activeTab "direct-messages") "--active")
}}
>
<UnreadDirectMessagesIndicator />
@ -76,7 +74,7 @@ export default class ChatFooter extends Component {
{{#if this.includeThreads}}
<DButton
@route="chat.threads"
@action={{fn @onClickTab "threads"}}
@icon="discourse-threads"
@translatedLabel={{i18n "chat.my_threads.title"}}
aria-label={{i18n "chat.my_threads.aria_label"}}
@ -84,7 +82,7 @@ export default class ChatFooter extends Component {
class={{concatClass
"btn-flat"
"c-footer__item"
(if (eq this.router.currentRouteName "chat.threads") "--active")
(if (eq @activeTab "threads") "--active")
}}
>
<UnreadThreadsIndicator />

View File

@ -1,27 +1,51 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import { service } from "@ember/service";
import { eq } from "truth-helpers";
import i18n from "discourse-common/helpers/i18n";
import ChannelsList from "discourse/plugins/chat/discourse/components/channels-list";
import ChannelsListDirect from "discourse/plugins/chat/discourse/components/channels-list-direct";
import ChannelsListPublic from "discourse/plugins/chat/discourse/components/channels-list-public";
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
import ChatFooter from "discourse/plugins/chat/discourse/components/chat-footer";
import UserThreads from "discourse/plugins/chat/discourse/components/user-threads";
export default class ChatDrawerRoutesChannels extends Component {
@service chat;
@service chatStateManager;
@tracked activeTab = "channels";
@action
onClickTab(tab) {
this.activeTab = tab;
}
<template>
<Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|>
<navbar.Title @title={{i18n "chat.heading"}} />
<navbar.Actions as |action|>
<action.ToggleDrawerButton />
<action.FullPageButton />
<action.CloseDrawerButton />
<navbar.Actions as |a|>
<a.ToggleDrawerButton />
<a.FullPageButton />
<a.CloseDrawerButton />
</navbar.Actions>
</Navbar>
{{#if this.chatStateManager.isDrawerExpanded}}
<div class="chat-drawer-content">
<ChannelsList />
{{#if (eq this.activeTab "channels")}}
<ChannelsListPublic />
{{else if (eq this.activeTab "direct-messages")}}
<ChannelsListDirect />
{{else if (eq this.activeTab "threads")}}
<UserThreads />
{{/if}}
</div>
<ChatFooter
@activeTab={{this.activeTab}}
@onClickTab={{this.onClickTab}}
/>
{{/if}}
</template>
}

View File

@ -0,0 +1,64 @@
import DButton from "discourse/components/d-button";
const EmptyChannelsList = <template>
<div class="channel-list-empty-message">
<svg
width="217"
height="163"
viewBox="0 0 217 163"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 128.5C12.5 128.5 30.5 148.5 69.5 142.5C86.9422 139.817 168.5 84.5 197.5 73"
stroke="var(--tertiary-high)"
stroke-linecap="round"
stroke-dasharray="6 6"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M53 15C39.7452 15 29 25.7452 29 39V96.4576C29 109.377 39.2089 119.913 52 120.437V137L74.3809 120.458H156C169.255 120.458 180 109.712 180 96.4576V39C180 25.7452 169.255 15 156 15H53Z"
fill="var(--secondary)"
/>
<path
d="M52 120.437H52.5V119.957L52.0205 119.938L52 120.437ZM52 137H51.5V137.991L52.2972 137.402L52 137ZM74.3809 120.458V119.958H74.2162L74.0837 120.056L74.3809 120.458ZM29.5 39C29.5 26.0213 40.0213 15.5 53 15.5V14.5C39.469 14.5 28.5 25.469 28.5 39H29.5ZM29.5 96.4576V39H28.5V96.4576H29.5ZM52.0205 119.938C39.4962 119.424 29.5 109.108 29.5 96.4576H28.5C28.5 109.647 38.9216 120.402 51.9795 120.937L52.0205 119.938ZM52.5 137V120.437H51.5V137H52.5ZM74.0837 120.056L51.7028 136.598L52.2972 137.402L74.6781 120.86L74.0837 120.056ZM156 119.958H74.3809V120.958H156V119.958ZM179.5 96.4576C179.5 109.436 168.979 119.958 156 119.958V120.958C169.531 120.958 180.5 109.989 180.5 96.4576H179.5ZM179.5 39V96.4576H180.5V39H179.5ZM156 15.5C168.979 15.5 179.5 26.0213 179.5 39H180.5C180.5 25.469 169.531 14.5 156 14.5V15.5ZM53 15.5H156V14.5H53V15.5Z"
fill="var(--tertiary-high)"
/>
<ellipse cx="104" cy="156" rx="96" ry="7" fill="var(--primary-low)" />
<path
d="M215.833 65.4678L209.881 73.3052C209.749 73.5001 209.525 73.6186 209.283 73.6212C209.158 73.6066 209.033 73.592 208.909 73.525C208.827 73.4803 208.755 73.415 208.695 73.3292L206.751 70.9161L204.851 71.9128C204.691 72.0124 204.513 71.9961 204.369 71.9179C204.142 71.7951 204.057 71.5092 204.18 71.2825L205.252 69.3042C205.341 69.1393 205.46 69.0174 205.598 68.959L213.217 65.6229L204.2 67.8014L202.557 65.7653C202.386 65.5394 202.333 65.244 202.478 64.9761C202.614 64.6765 202.849 64.5374 203.143 64.5366L215.222 64.2836C215.495 64.2716 215.784 64.4279 215.912 64.6838C216.04 64.9397 216.009 65.2428 215.833 65.4678Z"
fill="var(--tertiary-high)"
/>
<ellipse
cx="148"
cy="18.4167"
rx="17"
ry="18.4167"
fill="var(--primary-low)"
/>
<path
d="M147.883 8.27081C149.963 8.27081 151.589 9.013 152.761 10.4974C154.157 12.2552 154.855 15.1702 154.855 19.2425C154.855 23.305 154.152 26.2249 152.746 28.0023C151.584 29.4671 149.963 30.1995 147.883 30.1995C145.793 30.1995 144.108 29.3987 142.829 27.7972C141.55 26.1859 140.91 23.3196 140.91 19.1985C140.91 15.1556 141.613 12.2454 143.02 10.4681C144.182 9.00323 145.803 8.27081 147.883 8.27081ZM147.883 11.6839C147.385 11.6839 146.94 11.845 146.55 12.1673C146.159 12.4798 145.856 13.0462 145.642 13.8665C145.358 14.931 145.217 16.723 145.217 19.2425C145.217 21.762 145.344 23.4954 145.598 24.4427C145.852 25.3802 146.169 26.0052 146.55 26.3177C146.94 26.6302 147.385 26.7864 147.883 26.7864C148.381 26.7864 148.825 26.6302 149.216 26.3177C149.606 25.9954 149.909 25.4241 150.124 24.6038C150.407 23.5491 150.549 21.762 150.549 19.2425C150.549 16.723 150.422 14.9944 150.168 14.0569C149.914 13.1097 149.592 12.4798 149.201 12.1673C148.82 11.845 148.381 11.6839 147.883 11.6839Z"
fill="var(--primary)"
/>
<path
d="M13.013 41.1986C13.4829 41.2395 13.9951 41.6417 14.228 42.2301C14.4696 42.8402 14.3716 43.484 14.057 43.8355L15.3252 47.0389C15.4373 47.3221 15.3665 47.6527 15.1482 47.8652C14.9298 48.0776 14.5988 48.1331 14.3251 48.0146L12.9866 47.4353C11.5265 46.8034 9.87709 46.7758 8.39524 47.3625L8.22091 47.4315L9.32521 50.2209C9.4805 50.6131 9.27441 51.0476 8.90395 51.1943L7.50927 51.7464C7.11702 51.9017 6.69115 51.7174 6.53585 51.3252L5.43155 48.5358C4.64704 48.8464 3.7953 48.4778 3.48472 47.6933L2.65649 45.6013C2.35453 44.8386 2.71451 43.965 3.49901 43.6545L6.46271 42.4811C7.94455 41.8945 9.12808 40.7453 9.7686 39.307L10.3478 37.9685C10.4663 37.6948 10.7455 37.5086 11.0502 37.514C11.3548 37.5195 11.6327 37.7119 11.7362 37.9734L13.013 41.1986ZM7.01486 43.8758L6.84052 43.9448L7.66875 46.0368L7.84309 45.9678C9.58644 45.2776 11.5232 45.2671 13.2748 45.9348L11.0058 40.2036C10.186 41.8893 8.75821 43.1856 7.01486 43.8758Z"
fill="var(--tertiary-high)"
/>
<path
d="M148.762 128.636C149.155 128.701 149.422 129.101 149.358 129.494C149.29 129.91 148.913 130.181 148.52 130.116C148.103 130.048 147.833 129.671 147.901 129.255C147.965 128.862 148.346 128.568 148.762 128.636ZM151.353 129.061C151.746 129.125 152.013 129.525 151.948 129.918C151.888 130.288 151.484 130.578 151.114 130.518C150.721 130.453 150.427 130.073 150.491 129.679C150.559 129.263 150.936 128.992 151.353 129.061ZM153.943 129.485C154.336 129.549 154.626 129.953 154.539 130.343C154.471 130.759 154.094 131.03 153.701 130.965C153.307 130.901 153.014 130.52 153.082 130.104C153.146 129.711 153.527 129.417 153.943 129.485ZM152.02 124.99C155.281 125.524 157.558 128.153 157.126 130.79C156.694 133.427 153.705 135.146 150.443 134.612C149.68 134.486 148.959 134.25 148.345 133.935C147.572 134.307 146.367 134.704 144.956 134.473C144.725 134.435 144.536 134.285 144.504 134.042C144.469 133.823 144.53 133.595 144.718 133.46C144.718 133.46 145.564 132.791 146.059 131.946C145.428 131.011 145.132 129.918 145.31 128.831C145.746 126.171 148.735 124.452 152.02 124.99ZM150.625 133.501C153.262 133.933 155.659 132.64 155.996 130.581C156.329 128.546 154.451 126.528 151.815 126.096C149.178 125.664 146.754 126.977 146.421 129.013C146.258 130.007 146.622 130.827 146.966 131.334L147.369 131.923L147.008 132.529C146.84 132.834 146.625 133.131 146.394 133.378C146.98 133.284 147.484 133.105 147.845 132.927L148.338 132.675L148.843 132.924C149.391 133.204 150.001 133.399 150.625 133.501Z"
fill="var(--tertiary-high)"
/>
</svg>
<span class="channel-title">{{@title}}</span>
{{#if @showCTA}}
<DButton @action={{@ctaAction}} class="btn btn-primary">
{{@ctaTitle}}
</DButton>
{{/if}}
</div>
</template>;
export default EmptyChannelsList;

View File

@ -1,4 +1,5 @@
import Controller from "@ember/controller";
import { action } from "@ember/object";
import { service } from "@ember/service";
import { FOOTER_NAV_ROUTES } from "discourse/plugins/chat/discourse/lib/chat-constants";
@ -23,6 +24,10 @@ export default class ChatController extends Controller {
return this.siteSettings.navigation_menu === "sidebar";
}
get activeTab() {
return this.router.currentRouteName.replace(/^chat\./, "");
}
get shouldUseChatFooter() {
return (
this.site.mobileView &&
@ -46,4 +51,9 @@ export default class ChatController extends Controller {
return modifierClasses.join(" ");
}
@action
onClickTab(tab) {
return this.router.transitionTo(`chat.${tab}`);
}
}

View File

@ -21,7 +21,10 @@
>
{{outlet}}
{{#if this.shouldUseChatFooter}}
<ChatFooter />
<ChatFooter
@activeTab={{this.activeTab}}
@onClickTab={{this.onClickTab}}
/>
{{/if}}
</div>
</div>

View File

@ -1,5 +1,5 @@
.chat-channel-preview-card {
margin: 1rem 1rem 2rem 1rem;
margin: 1rem 0 2rem 0;
padding: 1.5rem 1rem;
background-color: var(--secondary-very-high);
display: flex;

View File

@ -0,0 +1,77 @@
.c-footer {
grid-area: footer;
background: var(--secondary);
border-top: 1px solid var(--primary-low);
display: flex;
align-items: flex-end;
justify-content: space-between;
position: sticky;
bottom: 0;
left: 0;
padding-bottom: env(safe-area-inset-bottom);
html.footer-nav-ipad &,
html.footer-nav-visible & {
padding-bottom: calc(
env(safe-area-inset-bottom) + var(--footer-nav-height, 0px)
);
}
&__item {
display: flex;
flex-direction: column;
align-items: center;
flex-basis: 33%;
flex-shrink: 0;
flex-grow: 1;
padding-block: 0.75rem;
height: 100%;
position: relative;
&.--active {
.d-icon,
.d-button-label {
color: var(--quaternary);
}
}
&:hover,
&:focus {
.discourse-no-touch &,
.discourse-touch & {
background-color: var(--primary-low);
.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);
}
.chat-channel-unread-indicator,
.chat-channel-unread-indicator.-urgent {
top: 0.25rem;
right: unset;
left: 50%;
margin-left: 0.75rem;
}
.chat-channel-unread-indicator:not(.-urgent) {
width: 11px;
height: 11px;
}
}
}

View File

@ -26,10 +26,12 @@
}
}
.channels-list {
.channels-list,
.channels-list-container {
padding-bottom: env(safe-area-inset-bottom);
position: relative;
@include chat-scrollbar();
height: 100%;
@include breakpoint(mobile-large) {
@include chat-scrollbar();
@ -63,26 +65,30 @@
}
.channel-list-empty-message {
margin: 1em 0.5em 0.5em 0.5em;
padding: 0 1em;
}
.chat-channel-divider {
padding: 2.5rem 1.5rem 0.5rem 1.5rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
justify-content: space-between;
font-weight: bold;
font-family: var(--heading-font-family);
font-size: var(--font-down-1);
color: var(--quaternary);
gap: 1rem;
height: 100%;
.channel-title {
line-height: var(--line-height-medium);
}
&:first-of-type {
padding-top: 1rem;
margin-bottom: 1rem;
}
}
}
.chat-channel-divider {
padding: 0.5rem 1rem 0.5rem 1rem;
display: flex;
align-items: center;
justify-content: space-between;
font-weight: bold;
font-family: var(--heading-font-family);
font-size: var(--font-down-1);
color: var(--quaternary);
.channel-title {
line-height: var(--line-height-medium);
}
}

View File

@ -70,3 +70,4 @@
@import "chat-audio-upload";
@import "chat-message-text";
@import "chat-messages-scroller";
@import "chat-footer";

View File

@ -1,4 +1,4 @@
.chat-drawer-container .channels-list {
.chat-drawer-container .channels-list-container {
.chat-channel-divider {
padding: 1.5rem 0.5rem 0.5rem 1rem;
font-size: var(--font-0);
@ -42,3 +42,7 @@
margin-left: 0.5em;
}
}
.chat-drawer-container .c-footer .chat-channel-unread-indicator.-urgent {
width: min-content;
}

View File

@ -4,6 +4,17 @@
height: calc(100vh - var(--header-offset));
border-right: 1px solid var(--primary-low);
background: var(--primary-very-low);
overflow-y: auto;
.channel-list-empty-message {
padding: 1rem;
text-align: center;
svg,
.channel-title {
display: none;
}
}
.chat-channel-divider {
padding: 2rem 1rem 0.5rem 1rem;
@ -40,5 +51,8 @@
}
}
}
.channels-list-container {
height: auto;
}
}
}

View File

@ -9,72 +9,6 @@
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;
padding-bottom: env(safe-area-inset-bottom);
html.footer-nav-ipad &,
html.footer-nav-visible & {
padding-bottom: calc(
env(safe-area-inset-bottom) + var(--footer-nav-height, 0px)
);
}
&__item {
display: flex;
flex-direction: column;
align-items: center;
flex-basis: 33%;
flex-shrink: 0;
padding-block: 0.75rem;
height: 100%;
position: relative;
&.--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);
}
.chat-channel-unread-indicator,
.chat-channel-unread-indicator.-urgent {
top: 0.25rem;
right: unset;
left: 50%;
margin-left: 0.75rem;
}
.chat-channel-unread-indicator:not(.-urgent) {
width: 11px;
height: 11px;
}
}
}
}
.has-full-page-chat div#reply-control {

View File

@ -101,7 +101,6 @@ en:
browse: "Browse channels"
create: "New channel"
click_to_join: "Click here to view available channels."
close: "Close"
remove: "Remove"
collapse: "Collapse Chat Drawer"
@ -233,8 +232,10 @@ 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."
no_direct_message_channels: "You have not joined any direct messages, yet!"
no_direct_message_channels_cta: "Start a conversation"
no_public_channels: "You have not joined any channels, yet!"
no_public_channels_cta: "Browse channels"
kicked_from_channel: "You can no longer access this channel."
only_chat_push_notifications:
title: "Only send chat push notifications"

View File

@ -15,6 +15,7 @@ RSpec.describe "Drawer - index", type: :system do
row = PageObjects::Components::Chat::ChannelRow.new(channel.id)
drawer_page.visit_index
drawer_page.click_direct_messages
expect(row).to exist
@ -33,6 +34,7 @@ RSpec.describe "Drawer - index", type: :system do
row = PageObjects::Components::Chat::ChannelRow.new(channel.id)
drawer_page.visit_index
drawer_page.click_direct_messages
expect(row).to exist

View File

@ -155,10 +155,10 @@ RSpec.describe "List channels | mobile", type: :system, mobile: true do
end
context "when no category channels" do
it "hides the section" do
it "shows the empty channel list" do
visit("/chat/channels")
expect(page).to have_no_css(".channels-list-container")
expect(page).to have_selector(".channel-list-empty-message")
end
context "when user can create channels" do

View File

@ -74,25 +74,30 @@ RSpec.describe "List channels | no sidebar", type: :system do
end
context "when no category channels" do
it "doesnt show the section" do
it "shows the empty channel list" do
visit("/chat")
expect(page).to have_no_css(".public-channels-section")
expect(page).to have_css(".c-list-empty-state")
end
it "does not show the create channel button" do
visit("/chat")
expect(page).to have_no_css(".-navbar__new-channel-button")
end
context "when user can create channels" do
before { current_user.update!(admin: true) }
it "shows the section" do
it "shows the new channel button" do
visit("/chat")
expect(page).to have_css(".public-channels-section")
expect(page).to have_css(".c-navbar__new-channel-button")
end
end
end
context "when no direct message channels" do
it "shows the section" do
it "shows the empty channel list" do
visit("/chat")
expect(page).to have_css(".direct-message-channels-section")
expect(page).to have_css(".c-list-empty-state")
end
end

View File

@ -60,8 +60,7 @@ module PageObjects
def visit_user_threads
visit("/chat/threads")
has_css?(".spinner")
has_no_css?(".spinner")
has_finished_loading?
end
def visit_thread(thread)

View File

@ -6,7 +6,7 @@ module PageObjects
class ChannelIndex < PageObjects::Components::Base
attr_reader :context
SELECTOR = ".channels-list"
SELECTOR = ".channels-list-container"
def initialize(context = nil)
@context = context

View File

@ -48,11 +48,11 @@ module PageObjects
end
def has_user_threads_section?
has_css?(".chat-channel-row.--threads[href='/chat/threads']")
has_css?("#c-footer-threads")
end
def has_no_user_threads_section?
has_no_css?(".chat-channel-row.--threads[href='/chat/threads']")
has_no_css?("#c-footer-threads")
end
def has_unread_user_threads?
@ -63,8 +63,12 @@ module PageObjects
has_no_css?(".chat-channel-row.--threads .c-unread-indicator")
end
def click_direct_messages
find("#c-footer-direct-messages").click
end
def click_user_threads
find(".chat-channel-row.--threads").click
find("#c-footer-threads").click
end
def maximize

View File

@ -133,13 +133,14 @@ RSpec.describe "User threads", type: :system do
end
context "when in drawer" do
before { SiteSetting.chat_threads_enabled = true }
context "when user is a member of at least one channel with threads" do
before { channel_1.add(current_user) }
it "shows a link to user threads" do
it "shows the user threads tab" do
visit("/")
chat_page.open_from_header
expect(drawer_page).to have_user_threads_section
end
end
@ -245,6 +246,8 @@ RSpec.describe "User threads", type: :system do
context "when in mobile", mobile: true do
before do
SiteSetting.chat_threads_enabled = true
last_message =
chat_thread_chain_bootstrap(
channel: channel_1,