mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 19:46:55 +08:00
DEV: implements <Chat::Navbar /> (#24917)
This new navbar component is used for every navbar in chat, full page or drawer, and any screen. This commit also uses this opportunity to correctly decouple drawer-routes from full page routes. This will avoid having this kind of properties in components: `@includeHeader={{false}}`. The header is now defined in the parent template using a navbar. Each route has now its own template wrapped in a div of the name of the route, eg: `<div class="c-routes-threads">..</div>`. The navbar API: ```gjs <Navbar as |navbar|> <navbar.BackButton /> <navbar.Title @title="Foo" /> <navbar.ChannelTitle @channel={{@channel}} /> <navbar.Actions as |action|> <action.CloseThreadButton /> </navbar.Actions> </navbar> ``` The full list of components is listed in `plugins/chat/assets/javascripts/discourse/components/navbar/index.gjs` and `plugins/chat/assets/javascripts/discourse/components/navbar/actions.gjs`. Visually the header is not changing much, only in drawer mode the background has been removed. This commit also introduces a `<List />` component to facilitate rendering lists in chat plugin.
This commit is contained in:
parent
a08691a599
commit
53b96638c5
|
@ -412,11 +412,6 @@ html {
|
|||
.reviewable .status span.approved {
|
||||
color: var(--success-hover);
|
||||
}
|
||||
|
||||
// Chat
|
||||
.chat-channel .open-drawer-btn {
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
}
|
||||
|
||||
// chat
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
<div class="chat-browse-view__header chat-full-page-header">
|
||||
{{#if this.site.mobileView}}
|
||||
<LinkTo
|
||||
@route="chat.index"
|
||||
class="chat-full-page-header__back-btn no-text btn-flat btn"
|
||||
title={{i18n "chat.browse.back"}}
|
||||
>
|
||||
{{d-icon "chevron-left"}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
|
||||
<span class="chat-browse-view__title">{{i18n "chat.browse.title"}}</span>
|
||||
|
||||
{{#if this.currentUser.staff}}
|
||||
<DButton
|
||||
@action={{this.createChannel}}
|
||||
@icon="plus"
|
||||
@label={{if this.site.desktopView "chat.create_channel.title"}}
|
||||
class={{concat-class
|
||||
"new-channel-btn"
|
||||
(if this.site.mobileView "btn-flat")
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<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
|
||||
{{did-insert (action this.focusFilterInput)}}
|
||||
@filterAction={{this.debouncedFiltering}}
|
||||
@icons={{hash right="search"}}
|
||||
@containerClass="filter-input"
|
||||
placeholder={{i18n "chat.browse.filter_input_placeholder"}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{{#if
|
||||
(and
|
||||
this.channelsCollection.fetchedOnce (not this.channelsCollection.length)
|
||||
)
|
||||
}}
|
||||
<div class="empty-state">
|
||||
<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>
|
||||
</div>
|
||||
{{else if this.channelsCollection.length}}
|
||||
<LoadMore
|
||||
@selector=".chat-channel-card"
|
||||
@action={{this.channelsCollection.load}}
|
||||
>
|
||||
<div class="chat-browse-view__content_wrapper">
|
||||
<div class="chat-browse-view__content">
|
||||
<div class="chat-browse-view__cards">
|
||||
{{#each this.channelsCollection as |channel|}}
|
||||
<ChatChannelCard @channel={{channel}} />
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ConditionalLoadingSpinner
|
||||
@condition={{this.channelsCollection.loading}}
|
||||
/>
|
||||
</LoadMore>
|
||||
{{/if}}
|
||||
</div>
|
|
@ -1,76 +0,0 @@
|
|||
import Component from "@ember/component";
|
||||
import { action, computed } from "@ember/object";
|
||||
import { schedule } from "@ember/runloop";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import ChatModalCreateChannel from "discourse/plugins/chat/discourse/components/chat/modal/create-channel";
|
||||
import ChatModalNewMessage from "discourse/plugins/chat/discourse/components/chat/modal/new-message";
|
||||
|
||||
const TABS = ["all", "open", "closed", "archived"];
|
||||
|
||||
export default class ChatBrowseView extends Component {
|
||||
@service chatApi;
|
||||
@service modal;
|
||||
|
||||
tagName = "";
|
||||
|
||||
didReceiveAttrs() {
|
||||
super.didReceiveAttrs(...arguments);
|
||||
|
||||
if (!this.channelsCollection) {
|
||||
this.set("channelsCollection", this.chatApi.channels());
|
||||
}
|
||||
|
||||
this.channelsCollection.load({
|
||||
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
|
||||
onScroll() {
|
||||
discourseDebounce(
|
||||
this,
|
||||
this.channelsCollection.load,
|
||||
{ filter: this.filter, status: this.status },
|
||||
INPUT_DELAY
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
debouncedFiltering(event) {
|
||||
this.set("channelsCollection", this.chatApi.channels());
|
||||
|
||||
discourseDebounce(
|
||||
this,
|
||||
this.channelsCollection.load,
|
||||
{ filter: event.target.value, status: this.status },
|
||||
INPUT_DELAY
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
createChannel() {
|
||||
this.modal.show(ChatModalCreateChannel);
|
||||
}
|
||||
|
||||
@action
|
||||
focusFilterInput(input) {
|
||||
schedule("afterRender", () => input?.focus());
|
||||
}
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import { inject as service } from "@ember/service";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import I18n from "discourse-i18n";
|
||||
import ChannelTitle from "discourse/plugins/chat/discourse/components/channel-title";
|
||||
import ChatModalEditChannelName from "discourse/plugins/chat/discourse/components/chat/modal/edit-channel-name";
|
||||
import ChatChannelStatus from "discourse/plugins/chat/discourse/components/chat-channel-status";
|
||||
|
||||
export default class ChatChannelMessageEmojiPicker extends Component {
|
||||
@service chatChannelInfoRouteOriginManager;
|
||||
@service site;
|
||||
@service modal;
|
||||
@service chatGuardian;
|
||||
|
||||
membersLabel = I18n.t("chat.channel_info.tabs.members");
|
||||
settingsLabel = I18n.t("chat.channel_info.tabs.settings");
|
||||
backToChannelLabel = I18n.t("chat.channel_info.back_to_all_channel");
|
||||
backToAllChannelsLabel = I18n.t("chat.channel_info.back_to_channel");
|
||||
|
||||
get showTabs() {
|
||||
return this.site.desktopView && this.args.channel.isOpen;
|
||||
}
|
||||
|
||||
get canEditChannel() {
|
||||
return (
|
||||
this.chatGuardian.canEditChatChannel() &&
|
||||
(this.args.channel.isCategoryChannel ||
|
||||
(this.args.channel.isDirectMessageChannel &&
|
||||
this.args.channel.chatable.group))
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
editChannelTitle() {
|
||||
return this.modal.show(ChatModalEditChannelName, {
|
||||
model: this.args.channel,
|
||||
});
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="chat-full-page-header">
|
||||
<div class="chat-channel-header-details">
|
||||
<div class="chat-full-page-header__left-actions">
|
||||
{{#if this.chatChannelInfoRouteOriginManager.isBrowse}}
|
||||
<LinkTo
|
||||
@route="chat.browse"
|
||||
class="chat-full-page-header__back-btn no-text btn-flat btn"
|
||||
title={{this.backToAllChannelsLabel}}
|
||||
>
|
||||
{{icon "chevron-left"}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo
|
||||
@route="chat.channel"
|
||||
@models={{@channel.routeModels}}
|
||||
class="chat-full-page-header__back-btn no-text btn-flat btn"
|
||||
title={{this.backToChannelLabel}}
|
||||
>
|
||||
{{icon "chevron-left"}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<ChannelTitle @channel={{@channel}} />
|
||||
|
||||
{{#if this.canEditChannel}}
|
||||
<DButton
|
||||
@icon="pencil-alt"
|
||||
class="btn-flat"
|
||||
@action={{this.editChannelTitle}}
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ChatChannelStatus @channel={{@channel}} />
|
||||
|
||||
<div class="chat-channel-info">
|
||||
{{#if this.showTabs}}
|
||||
<nav class="chat-channel-info__nav">
|
||||
<ul class="nav nav-pills">
|
||||
<li>
|
||||
<LinkTo
|
||||
@route="chat.channel.info.settings"
|
||||
@model={{@channel}}
|
||||
@replace={{true}}
|
||||
>
|
||||
{{this.settingsLabel}}
|
||||
</LinkTo>
|
||||
</li>
|
||||
<li>
|
||||
<LinkTo
|
||||
@route="chat.channel.info.members"
|
||||
@model={{@channel}}
|
||||
@replace={{true}}
|
||||
>
|
||||
{{this.membersLabel}}
|
||||
</LinkTo>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{{/if}}
|
||||
|
||||
{{outlet}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -21,6 +21,7 @@ import discourseDebounce from "discourse-common/lib/debounce";
|
|||
import { bind } from "discourse-common/utils/decorators";
|
||||
import and from "truth-helpers/helpers/and";
|
||||
import not from "truth-helpers/helpers/not";
|
||||
import ChatChannelStatus from "discourse/plugins/chat/discourse/components/chat-channel-status";
|
||||
import ChatChannelSubscriptionManager from "discourse/plugins/chat/discourse/lib/chat-channel-subscription-manager";
|
||||
import {
|
||||
FUTURE,
|
||||
|
@ -45,7 +46,6 @@ import ChatComposerChannel from "./chat/composer/channel";
|
|||
import ChatScrollToBottomArrow from "./chat/scroll-to-bottom-arrow";
|
||||
import ChatSelectionManager from "./chat/selection-manager";
|
||||
import ChatChannelPreviewCard from "./chat-channel-preview-card";
|
||||
import ChatFullPageHeader from "./chat-full-page-header";
|
||||
import ChatMentionWarnings from "./chat-mention-warnings";
|
||||
import Message from "./chat-message";
|
||||
import ChatNotices from "./chat-notices";
|
||||
|
@ -719,14 +719,9 @@ export default class ChatChannel extends Component {
|
|||
{{didUpdate this.loadMessages @targetMessageId}}
|
||||
data-id={{@channel.id}}
|
||||
>
|
||||
<ChatFullPageHeader
|
||||
@channel={{@channel}}
|
||||
@onCloseFullScreen={{this.onCloseFullScreen}}
|
||||
@displayed={{this.includeHeader}}
|
||||
/>
|
||||
|
||||
<ChatChannelStatus @channel={{@channel}} />
|
||||
<ChatNotices @channel={{@channel}} />
|
||||
|
||||
<ChatMentionWarnings />
|
||||
|
||||
<div
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||
import { inject as service } from "@ember/service";
|
||||
import I18n from "discourse-i18n";
|
||||
import and from "truth-helpers/helpers/and";
|
||||
import ChatDrawerHeader from "discourse/plugins/chat/discourse/components/chat-drawer/header";
|
||||
import ChatDrawerHeaderBackLink from "discourse/plugins/chat/discourse/components/chat-drawer/header/back-link";
|
||||
import ChatDrawerHeaderRightActions from "discourse/plugins/chat/discourse/components/chat-drawer/header/right-actions";
|
||||
import ChatDrawerHeaderTitle from "discourse/plugins/chat/discourse/components/chat-drawer/header/title";
|
||||
import ChatThreadList from "discourse/plugins/chat/discourse/components/chat-thread-list";
|
||||
|
||||
export default class ChatDrawerChannelThreads extends Component {
|
||||
@service appEvents;
|
||||
@service chat;
|
||||
@service chatStateManager;
|
||||
@service chatChannelsManager;
|
||||
|
||||
backLinkTitle = I18n.t("chat.return_to_list");
|
||||
|
||||
@action
|
||||
fetchChannel() {
|
||||
if (!this.args.params?.channelId) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.chatChannelsManager
|
||||
.find(this.args.params.channelId)
|
||||
.then((channel) => {
|
||||
this.chat.activeChannel = channel;
|
||||
});
|
||||
}
|
||||
|
||||
<template>
|
||||
<ChatDrawerHeader @toggleExpand={{@drawerActions.toggleExpand}}>
|
||||
{{#if
|
||||
(and this.chatStateManager.isDrawerExpanded this.chat.activeChannel)
|
||||
}}
|
||||
<div class="chat-drawer-header__left-actions">
|
||||
<div class="chat-drawer-header__top-line">
|
||||
<ChatDrawerHeaderBackLink
|
||||
@route="chat.channel"
|
||||
@title={{this.backLinkTitle}}
|
||||
@routeModels={{this.chat.activeChannel.routeModels}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<ChatDrawerHeaderTitle
|
||||
@title="chat.threads.list"
|
||||
@icon="discourse-threads"
|
||||
@channelName={{this.chat.activeChannel.title}}
|
||||
/>
|
||||
|
||||
<ChatDrawerHeaderRightActions @drawerActions={{@drawerActions}} />
|
||||
</ChatDrawerHeader>
|
||||
|
||||
{{#if this.chatStateManager.isDrawerExpanded}}
|
||||
<div class="chat-drawer-content" {{didInsert this.fetchChannel}}>
|
||||
{{#if this.chat.activeChannel}}
|
||||
<ChatThreadList
|
||||
@channel={{this.chat.activeChannel}}
|
||||
@includeHeader={{false}}
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { on } from "@ember/modifier";
|
||||
import { inject as service } from "@ember/service";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
|
||||
export default class ChatDrawerHeader extends Component {
|
||||
@service chatStateManager;
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable no-invalid-interactive }}
|
||||
<div
|
||||
role="region"
|
||||
aria-label={{i18n "chat.aria_roles.header"}}
|
||||
class="chat-drawer-header"
|
||||
{{on "click" @toggleExpand}}
|
||||
title={{if
|
||||
this.chatStateManager.isDrawerExpanded
|
||||
(i18n "chat.collapse")
|
||||
(i18n "chat.expand")
|
||||
}}
|
||||
>
|
||||
{{yield}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { array } from "@ember/helper";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import { inject as service } from "@ember/service";
|
||||
import dIcon from "discourse-common/helpers/d-icon";
|
||||
import or from "truth-helpers/helpers/or";
|
||||
|
||||
export default class ChatDrawerHeaderBackLink extends Component {
|
||||
@service chatStateManager;
|
||||
|
||||
<template>
|
||||
<LinkTo
|
||||
title={{@title}}
|
||||
class="chat-drawer-header__back-btn"
|
||||
@route={{@route}}
|
||||
@models={{or @routeModels (array)}}
|
||||
>
|
||||
{{dIcon "chevron-left"}}
|
||||
</LinkTo>
|
||||
</template>
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { on } from "@ember/modifier";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import { inject as service } from "@ember/service";
|
||||
import ChannelTitle from "../../channel-title";
|
||||
|
||||
export default class ChatDrawerChannelHeaderTitle extends Component {
|
||||
@service chatStateManager;
|
||||
|
||||
<template>
|
||||
{{#if @channel}}
|
||||
{{#if this.chatStateManager.isDrawerExpanded}}
|
||||
<LinkTo
|
||||
@route={{if
|
||||
@channel.isDirectMessageChannel
|
||||
"chat.channel.info.settings"
|
||||
"chat.channel.info.members"
|
||||
}}
|
||||
@models={{@channel.routeModels}}
|
||||
class="chat-drawer-header__title"
|
||||
>
|
||||
<div class="chat-drawer-header__top-line">
|
||||
<ChannelTitle @channel={{@channel}} />
|
||||
</div>
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<div
|
||||
role="button"
|
||||
{{on "click" @drawerActions.toggleExpand}}
|
||||
class="chat-drawer-header__title"
|
||||
>
|
||||
<div class="chat-drawer-header__top-line">
|
||||
<ChannelTitle @channel={{@channel}}>
|
||||
{{#if @channel.tracking.unreadCount}}
|
||||
<span class="chat-unread-count">
|
||||
{{@channel.tracking.unreadCount}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</ChannelTitle>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
import DButton from "discourse/components/d-button";
|
||||
|
||||
const CloseButton = <template>
|
||||
<DButton
|
||||
@icon="times"
|
||||
@action={{@close}}
|
||||
@title="chat.close"
|
||||
class="btn-flat btn-link chat-drawer-header__close-btn"
|
||||
/>
|
||||
</template>;
|
||||
|
||||
export default CloseButton;
|
|
@ -1,18 +0,0 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { inject as service } from "@ember/service";
|
||||
import DButton from "discourse/components/d-button";
|
||||
|
||||
export default class ChatDrawerHeaderFullPageButton extends Component {
|
||||
@service chatStateManager;
|
||||
|
||||
<template>
|
||||
{{#if this.chatStateManager.isDrawerExpanded}}
|
||||
<DButton
|
||||
@icon="discourse-expand"
|
||||
class="btn-flat btn-link chat-drawer-header__full-screen-btn"
|
||||
@title="chat.open_full_page"
|
||||
@action={{@openInFullPage}}
|
||||
/>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { inject as service } from "@ember/service";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import BackLink from "./back-link";
|
||||
|
||||
export default class ChatDrawerHeaderLeftActions extends Component {
|
||||
@service chatStateManager;
|
||||
|
||||
<template>
|
||||
{{#if this.chatStateManager.isDrawerExpanded}}
|
||||
<div class="chat-drawer-header__left-actions">
|
||||
<div class="chat-drawer-header__top-line">
|
||||
<BackLink @route="chat" @title={{i18n "chat.return_to_list"}} />
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { inject as service } from "@ember/service";
|
||||
import ThreadsListButton from "../../chat/thread/threads-list-button";
|
||||
import CloseButton from "./close-button";
|
||||
import FullPageButton from "./full-page-button";
|
||||
import ToggleExpandButton from "./toggle-expand-button";
|
||||
|
||||
export default class ChatDrawerHeaderRightActions extends Component {
|
||||
@service chat;
|
||||
|
||||
get showThreadsListButton() {
|
||||
return this.chat.activeChannel?.threadingEnabled;
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="chat-drawer-header__right-actions">
|
||||
<div class="chat-drawer-header__top-line">
|
||||
{{#if this.showThreadsListButton}}
|
||||
<ThreadsListButton @channel={{this.chat.activeChannel}} />
|
||||
{{/if}}
|
||||
|
||||
<ToggleExpandButton @toggleExpand={{@drawerActions.toggleExpand}} />
|
||||
|
||||
<FullPageButton @openInFullPage={{@drawerActions.openInFullPage}} />
|
||||
|
||||
<CloseButton @close={{@drawerActions.close}} />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
import Component from "@glimmer/component";
|
||||
import replaceEmoji from "discourse/helpers/replace-emoji";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
export default class ChatDrawerHeaderTitle extends Component {
|
||||
get headerTitle() {
|
||||
if (this.args.title) {
|
||||
return I18n.t(this.args.title);
|
||||
}
|
||||
return replaceEmoji(this.args.translatedTitle);
|
||||
}
|
||||
|
||||
get showChannel() {
|
||||
return this.args.channelName ?? false;
|
||||
}
|
||||
|
||||
get showIcon() {
|
||||
return this.args.icon ?? false;
|
||||
}
|
||||
|
||||
<template>
|
||||
<span class="chat-drawer-header__title">
|
||||
<div class="chat-drawer-header__top-line">
|
||||
<span class="chat-drawer-header__icon">
|
||||
{{#if this.showIcon}}
|
||||
{{icon @icon}}
|
||||
{{/if}}
|
||||
</span>
|
||||
|
||||
<span class="chat-drawer-header__title-text">{{this.headerTitle}}</span>
|
||||
|
||||
{{#if this.showChannel}}
|
||||
<span class="chat-drawer-header__divider">-</span>
|
||||
<span class="chat-drawer-header__channel-name">{{@channelName}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</span>
|
||||
</template>
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { inject as service } from "@ember/service";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import ChannelsList from "../channels-list";
|
||||
import Header from "./header";
|
||||
import RightActions from "./header/right-actions";
|
||||
|
||||
export default class ChatDrawerIndex extends Component {
|
||||
@service chatStateManager;
|
||||
|
||||
<template>
|
||||
<Header @toggleExpand={{@drawerActions.toggleExpand}}>
|
||||
<div class="chat-drawer-header__title">
|
||||
<div class="chat-drawer-header__top-line">
|
||||
{{i18n "chat.heading"}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<RightActions @drawerActions={{@drawerActions}} />
|
||||
</Header>
|
||||
|
||||
{{#if this.chatStateManager.isDrawerExpanded}}
|
||||
<div class="chat-drawer-content">
|
||||
<ChannelsList />
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { inject as service } from "@ember/service";
|
||||
import I18n from "discourse-i18n";
|
||||
import ChatDrawerHeader from "discourse/plugins/chat/discourse/components/chat-drawer/header";
|
||||
import ChatDrawerHeaderBackLink from "discourse/plugins/chat/discourse/components/chat-drawer/header/back-link";
|
||||
import ChatDrawerHeaderRightActions from "discourse/plugins/chat/discourse/components/chat-drawer/header/right-actions";
|
||||
import ChatDrawerHeaderTitle from "discourse/plugins/chat/discourse/components/chat-drawer/header/title";
|
||||
import UserThreads from "discourse/plugins/chat/discourse/components/user-threads";
|
||||
|
||||
export default class ChatDrawerThreads extends Component {
|
||||
@service appEvents;
|
||||
@service chat;
|
||||
@service chatStateManager;
|
||||
@service chatChannelsManager;
|
||||
|
||||
backLinkTitle = I18n.t("chat.return_to_list");
|
||||
|
||||
<template>
|
||||
<ChatDrawerHeader @toggleExpand={{@drawerActions.toggleExpand}}>
|
||||
|
||||
{{#if this.chatStateManager.isDrawerExpanded}}
|
||||
<div class="chat-drawer-header__left-actions">
|
||||
<div class="chat-drawer-header__top-line">
|
||||
<ChatDrawerHeaderBackLink
|
||||
@route="chat"
|
||||
@title={{this.backLink.title}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<ChatDrawerHeaderTitle
|
||||
@title="chat.threads.list"
|
||||
@icon="discourse-threads"
|
||||
@channelName={{this.chat.activeChannel.title}}
|
||||
/>
|
||||
|
||||
<ChatDrawerHeaderRightActions @drawerActions={{@drawerActions}} />
|
||||
</ChatDrawerHeader>
|
||||
|
||||
{{#if this.chatStateManager.isDrawerExpanded}}
|
||||
<div class="chat-drawer-content">
|
||||
<UserThreads />
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { on } from "@ember/modifier";
|
||||
import { action } from "@ember/object";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import { inject as service } from "@ember/service";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import and from "truth-helpers/helpers/and";
|
||||
import or from "truth-helpers/helpers/or";
|
||||
import ChannelTitle from "discourse/plugins/chat/discourse/components/channel-title";
|
||||
import ChatModalEditChannelName from "discourse/plugins/chat/discourse/components/chat/modal/edit-channel-name";
|
||||
import ThreadsListButton from "discourse/plugins/chat/discourse/components/chat/thread/threads-list-button";
|
||||
import ChatChannelStatus from "discourse/plugins/chat/discourse/components/chat-channel-status";
|
||||
|
||||
export default class ChatFullPageHeader extends Component {
|
||||
@service chatStateManager;
|
||||
@service modal;
|
||||
@service router;
|
||||
@service site;
|
||||
|
||||
get displayed() {
|
||||
return this.args.displayed ?? true;
|
||||
}
|
||||
|
||||
get showThreadsListButton() {
|
||||
return (
|
||||
this.args.channel.threadingEnabled &&
|
||||
this.router.currentRoute.name !== "chat.channel.threads" &&
|
||||
this.router.currentRoute.name !== "chat.channel.thread.index" &&
|
||||
this.router.currentRoute.name !== "chat.channel.thread"
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
editChannelTitle() {
|
||||
return this.modal.show(ChatModalEditChannelName, {
|
||||
model: this.args.channel,
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
trapMouse(event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable no-invalid-interactive }}
|
||||
{{#if (and this.chatStateManager.isFullPageActive this.displayed)}}
|
||||
<div
|
||||
class={{concatClass
|
||||
"chat-full-page-header"
|
||||
(unless @channel.isFollowing "-not-following")
|
||||
}}
|
||||
{{on "mousemove" this.trapMouse}}
|
||||
>
|
||||
<div class="chat-channel-header-details">
|
||||
{{#if this.site.mobileView}}
|
||||
<div class="chat-full-page-header__left-actions">
|
||||
<LinkTo
|
||||
@route="chat"
|
||||
class="chat-full-page-header__back-btn no-text btn-flat"
|
||||
>
|
||||
{{icon "chevron-left"}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<LinkTo
|
||||
@route="chat.channel.info"
|
||||
@models={{@channel.routeModels}}
|
||||
class="chat-channel-title-wrapper"
|
||||
>
|
||||
<ChannelTitle @channel={{@channel}} />
|
||||
</LinkTo>
|
||||
|
||||
{{#if (or @channel.threadingEnabled this.site.desktopView)}}
|
||||
<div class="chat-full-page-header__right-actions">
|
||||
{{#if this.site.desktopView}}
|
||||
<DButton
|
||||
@icon="discourse-compress"
|
||||
@title="chat.close_full_page"
|
||||
class="open-drawer-btn btn-flat"
|
||||
@action={{@onCloseFullScreen}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.showThreadsListButton}}
|
||||
<ThreadsListButton @channel={{@channel}} />
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ChatChannelStatus @channel={{@channel}} />
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -98,12 +98,15 @@ export default class ChatMessageThreadIndicator extends Component {
|
|||
|
||||
@bind
|
||||
openThread(event) {
|
||||
if (event.type === "keydown" && event.key !== "Enter") {
|
||||
if (event?.type === "keydown" && event?.key !== "Enter") {
|
||||
return;
|
||||
}
|
||||
|
||||
// handle middle mouse
|
||||
if (event.type === "mousedown" && (event.which === 2 || event.shiftKey)) {
|
||||
if (
|
||||
event?.type === "mousedown" &&
|
||||
(event?.which === 2 || event?.shiftKey)
|
||||
) {
|
||||
window.open(
|
||||
getURL(
|
||||
this.router.urlFor(
|
||||
|
|
|
@ -41,7 +41,7 @@ export default class ChatRetentionReminder extends Component {
|
|||
<DButton
|
||||
@action={{this.dismiss}}
|
||||
@icon="times"
|
||||
class="btn-flat dismiss-btn"
|
||||
class="btn no-text btn-icon btn-flat no-text dismiss-btn"
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
|
@ -7,7 +7,6 @@ import isElementInViewport from "discourse/lib/is-element-in-viewport";
|
|||
import { bind } from "discourse-common/utils/decorators";
|
||||
import I18n from "discourse-i18n";
|
||||
import eq from "truth-helpers/helpers/eq";
|
||||
import ChatThreadListHeader from "discourse/plugins/chat/discourse/components/chat/thread-list/header";
|
||||
import ChatThreadListItem from "discourse/plugins/chat/discourse/components/chat/thread-list/item";
|
||||
import ChatTrackMessage from "discourse/plugins/chat/discourse/modifiers/chat/track-message";
|
||||
|
||||
|
@ -179,10 +178,6 @@ export default class ChatThreadList extends Component {
|
|||
<template>
|
||||
{{#if this.shouldRender}}
|
||||
<div class="chat-thread-list" {{this.subscribe @channel}}>
|
||||
{{#if @includeHeader}}
|
||||
<ChatThreadListHeader @channel={{@channel}} />
|
||||
{{/if}}
|
||||
|
||||
<div class="chat-thread-list__items" {{this.fill}}>
|
||||
|
||||
{{#each this.sortedThreads key="id" as |thread|}}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import NotificationsButtonComponent from "select-kit/components/notifications-button";
|
||||
import { threadNotificationButtonLevels } from "discourse/plugins/chat/discourse/lib/chat-notification-levels";
|
||||
|
||||
export default NotificationsButtonComponent.extend({
|
||||
pluginApiIdentifiers: ["thread-notifications-button"],
|
||||
classNames: ["thread-notifications-button"],
|
||||
content: threadNotificationButtonLevels,
|
||||
|
||||
selectKitOptions: {
|
||||
i18nPrefix: "chat.thread.notifications",
|
||||
showFullTitle: false,
|
||||
btnCustomClasses: "btn-flat",
|
||||
},
|
||||
});
|
|
@ -36,7 +36,6 @@ import ChatScrollableList from "../modifiers/chat/scrollable-list";
|
|||
import ChatComposerThread from "./chat/composer/thread";
|
||||
import ChatScrollToBottomArrow from "./chat/scroll-to-bottom-arrow";
|
||||
import ChatSelectionManager from "./chat/selection-manager";
|
||||
import ChatThreadHeader from "./chat/thread/header";
|
||||
import Message from "./chat-message";
|
||||
import ChatSkeleton from "./chat-skeleton";
|
||||
import ChatUploadDropZone from "./chat-upload-drop-zone";
|
||||
|
@ -493,10 +492,6 @@ export default class ChatThread extends Component {
|
|||
{{didInsert this.setup}}
|
||||
{{willDestroy this.teardown}}
|
||||
>
|
||||
{{#if @includeHeader}}
|
||||
<ChatThreadHeader @channel={{@thread.channel}} @thread={{@thread}} />
|
||||
{{/if}}
|
||||
|
||||
<div
|
||||
class="chat-thread__body popper-viewport chat-messages-scroll"
|
||||
{{didInsert this.setScrollable}}
|
||||
|
|
|
@ -4,24 +4,20 @@ import { action } from "@ember/object";
|
|||
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||
import didUpdate from "@ember/render-modifiers/modifiers/did-update";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import I18n from "discourse-i18n";
|
||||
import and from "truth-helpers/helpers/and";
|
||||
import ChatDrawerHeader from "discourse/plugins/chat/discourse/components/chat-drawer/header";
|
||||
import ChatDrawerHeaderBackLink from "discourse/plugins/chat/discourse/components/chat-drawer/header/back-link";
|
||||
import ChatDrawerHeaderRightActions from "discourse/plugins/chat/discourse/components/chat-drawer/header/right-actions";
|
||||
import ChatDrawerHeaderTitle from "discourse/plugins/chat/discourse/components/chat-drawer/header/title";
|
||||
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
|
||||
import ChatThread from "discourse/plugins/chat/discourse/components/chat-thread";
|
||||
|
||||
export default class ChatDrawerThread extends Component {
|
||||
@service appEvents;
|
||||
export default class ChatDrawerRoutesChannelThread extends Component {
|
||||
@service chat;
|
||||
@service chatStateManager;
|
||||
@service chatChannelsManager;
|
||||
@service chatHistory;
|
||||
|
||||
get backLink() {
|
||||
get backButton() {
|
||||
const link = {
|
||||
models: this.chat.activeChannel.routeModels,
|
||||
models: this.chat.activeChannel?.routeModels,
|
||||
};
|
||||
|
||||
if (this.chatHistory.previousRoute?.name === "chat.channel.threads") {
|
||||
|
@ -47,14 +43,16 @@ export default class ChatDrawerThread extends Component {
|
|||
}
|
||||
|
||||
@action
|
||||
fetchChannelAndThread() {
|
||||
async fetchChannelAndThread() {
|
||||
if (!this.args.params?.channelId || !this.args.params?.threadId) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.chatChannelsManager
|
||||
.find(this.args.params.channelId)
|
||||
.then((channel) => {
|
||||
try {
|
||||
const channel = await this.chatChannelsManager.find(
|
||||
this.args.params.channelId
|
||||
);
|
||||
|
||||
this.chat.activeChannel = channel;
|
||||
|
||||
channel.threadsManager
|
||||
|
@ -62,29 +60,25 @@ export default class ChatDrawerThread extends Component {
|
|||
.then((thread) => {
|
||||
this.chat.activeChannel.activeThread = thread;
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
popupAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
<ChatDrawerHeader @toggleExpand={{@drawerActions.toggleExpand}}>
|
||||
{{#if
|
||||
(and this.chatStateManager.isDrawerExpanded this.chat.activeChannel)
|
||||
}}
|
||||
<div class="chat-drawer-header__left-actions">
|
||||
<div class="chat-drawer-header__top-line">
|
||||
<ChatDrawerHeaderBackLink
|
||||
@route={{this.backLink.route}}
|
||||
@title={{this.backLink.title}}
|
||||
@routeModels={{this.backLink.models}}
|
||||
<Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|>
|
||||
<navbar.BackButton
|
||||
@title={{this.backButton.title}}
|
||||
@route={{this.backButton.route}}
|
||||
@routeModels={{this.backButton.models}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<ChatDrawerHeaderTitle @translatedTitle={{this.threadTitle}} />
|
||||
|
||||
<ChatDrawerHeaderRightActions @drawerActions={{@drawerActions}} />
|
||||
</ChatDrawerHeader>
|
||||
<navbar.Title @title={{this.threadTitle}} @icon="discourse-threads" />
|
||||
<navbar.Actions as |action|>
|
||||
<action.ToggleDrawerButton />
|
||||
<action.FullPageButton />
|
||||
<action.CloseDrawerButton />
|
||||
</navbar.Actions>
|
||||
</Navbar>
|
||||
|
||||
{{#if this.chatStateManager.isDrawerExpanded}}
|
||||
<div
|
|
@ -0,0 +1,68 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||
import { inject as service } from "@ember/service";
|
||||
import replaceEmoji from "discourse/helpers/replace-emoji";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import htmlSafe from "discourse-common/helpers/html-safe";
|
||||
import I18n from "discourse-i18n";
|
||||
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
|
||||
import ChatThreadList from "discourse/plugins/chat/discourse/components/chat-thread-list";
|
||||
|
||||
export default class ChatDrawerRoutesChannelThreads extends Component {
|
||||
@service chat;
|
||||
@service chatChannelsManager;
|
||||
|
||||
backLinkTitle = I18n.t("chat.return_to_list");
|
||||
|
||||
get title() {
|
||||
return htmlSafe(
|
||||
I18n.t("chat.threads.list") +
|
||||
" - " +
|
||||
replaceEmoji(this.chat.activeChannel.title)
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
async fetchChannel() {
|
||||
if (!this.args.params?.channelId) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const channel = await this.chatChannelsManager.find(
|
||||
this.args.params.channelId
|
||||
);
|
||||
this.chat.activeChannel = channel;
|
||||
} catch (error) {
|
||||
popupAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.chat.activeChannel}}
|
||||
<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 |action|>
|
||||
<action.ToggleDrawerButton />
|
||||
<action.FullPageButton />
|
||||
<action.CloseDrawerButton />
|
||||
</navbar.Actions>
|
||||
</Navbar>
|
||||
{{/if}}
|
||||
|
||||
<div class="chat-drawer-content" {{didInsert this.fetchChannel}}>
|
||||
{{#if this.chat.activeChannel}}
|
||||
<ChatThreadList
|
||||
@channel={{this.chat.activeChannel}}
|
||||
@includeHeader={{false}}
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -4,14 +4,10 @@ import { action } from "@ember/object";
|
|||
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||
import didUpdate from "@ember/render-modifiers/modifiers/did-update";
|
||||
import { inject as service } from "@ember/service";
|
||||
import ChatChannel from "../chat-channel";
|
||||
import Header from "./header";
|
||||
import ChannelTitle from "./header/channel-title";
|
||||
import LeftActions from "./header/left-actions";
|
||||
import RightActions from "./header/right-actions";
|
||||
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
|
||||
import ChatChannel from "discourse/plugins/chat/discourse/components/chat-channel";
|
||||
|
||||
export default class ChatDrawerChannel extends Component {
|
||||
@service appEvents;
|
||||
export default class ChatDrawerRoutesChannel extends Component {
|
||||
@service chat;
|
||||
@service chatStateManager;
|
||||
@service chatChannelsManager;
|
||||
|
@ -30,16 +26,16 @@ export default class ChatDrawerChannel extends Component {
|
|||
}
|
||||
|
||||
<template>
|
||||
<Header @toggleExpand={{@drawerActions.toggleExpand}}>
|
||||
<LeftActions />
|
||||
|
||||
<ChannelTitle
|
||||
@channel={{this.chat.activeChannel}}
|
||||
@drawerActions={{@drawerActions}}
|
||||
/>
|
||||
|
||||
<RightActions @drawerActions={{@drawerActions}} />
|
||||
</Header>
|
||||
<Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|>
|
||||
<navbar.BackButton />
|
||||
<navbar.ChannelTitle @channel={{this.chat.activeChannel}} />
|
||||
<navbar.Actions as |action|>
|
||||
<action.ThreadsListButton @channel={{this.chat.activeChannel}} />
|
||||
<action.ToggleDrawerButton />
|
||||
<action.FullPageButton />
|
||||
<action.CloseDrawerButton />
|
||||
</navbar.Actions>
|
||||
</Navbar>
|
||||
|
||||
{{#if this.chatStateManager.isDrawerExpanded}}
|
||||
<div
|
|
@ -0,0 +1,27 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { inject as service } from "@ember/service";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import ChannelsList from "discourse/plugins/chat/discourse/components/channels-list";
|
||||
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
|
||||
|
||||
export default class ChatDrawerRoutesChannels extends Component {
|
||||
@service chat;
|
||||
@service chatStateManager;
|
||||
|
||||
<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>
|
||||
</Navbar>
|
||||
|
||||
{{#if this.chatStateManager.isDrawerExpanded}}
|
||||
<div class="chat-drawer-content">
|
||||
<ChannelsList />
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { inject as service } from "@ember/service";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import I18n from "discourse-i18n";
|
||||
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
|
||||
import UserThreads from "discourse/plugins/chat/discourse/components/user-threads";
|
||||
|
||||
export default class ChatDrawerRoutesThreads extends Component {
|
||||
@service chat;
|
||||
@service chatStateManager;
|
||||
|
||||
backButtonTitle = I18n.t("chat.return_to_list");
|
||||
|
||||
<template>
|
||||
<Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|>
|
||||
<navbar.BackButton @title={{this.backButtonTitle}} />
|
||||
<navbar.Title
|
||||
@title={{i18n "chat.threads.list"}}
|
||||
@icon="discourse-threads"
|
||||
as |title|
|
||||
>
|
||||
<title.SubTitle @title={{this.chat.activeChannel.title}} />
|
||||
</navbar.Title>
|
||||
<navbar.Actions as |action|>
|
||||
<action.ThreadsListButton />
|
||||
<action.ToggleDrawerButton />
|
||||
<action.FullPageButton />
|
||||
<action.CloseDrawerButton />
|
||||
</navbar.Actions>
|
||||
</Navbar>
|
||||
|
||||
{{#if this.chatStateManager.isDrawerExpanded}}
|
||||
<div class="chat-drawer-content">
|
||||
<UserThreads />
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import Component from "@glimmer/component";
|
||||
|
||||
export default class EmptyState extends Component {
|
||||
<template>
|
||||
<div class="c-list-empty-state" ...attributes>
|
||||
{{yield}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { hash } from "@ember/helper";
|
||||
import { action } from "@ember/object";
|
||||
import { modifier } from "ember-modifier";
|
||||
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
|
||||
import isElementInViewport from "discourse/lib/is-element-in-viewport";
|
||||
import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import EmptyState from "./empty-state";
|
||||
import Item from "./item";
|
||||
|
||||
export default class List extends Component {
|
||||
loadMore = modifier((element) => {
|
||||
this.intersectionObserver = new IntersectionObserver(this.loadCollection);
|
||||
this.intersectionObserver.observe(element);
|
||||
|
||||
return () => {
|
||||
this.intersectionObserver.disconnect();
|
||||
};
|
||||
});
|
||||
|
||||
fill = modifier((element) => {
|
||||
this.resizeObserver = new ResizeObserver(() => {
|
||||
if (isElementInViewport(element)) {
|
||||
this.loadCollection();
|
||||
}
|
||||
});
|
||||
|
||||
this.resizeObserver.observe(element);
|
||||
|
||||
return () => {
|
||||
this.resizeObserver.disconnect();
|
||||
};
|
||||
});
|
||||
|
||||
get itemComponent() {
|
||||
return this.args.itemComponent ?? Item;
|
||||
}
|
||||
|
||||
get emptyStateComponent() {
|
||||
return EmptyState;
|
||||
}
|
||||
|
||||
@action
|
||||
loadCollection() {
|
||||
discourseDebounce(this, this.debouncedLoadCollection, INPUT_DELAY);
|
||||
}
|
||||
|
||||
async debouncedLoadCollection() {
|
||||
await this.args.collection.load({ limit: 10 });
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="c-list">
|
||||
<div {{this.fill}} ...attributes>
|
||||
{{#each @collection.items as |item|}}
|
||||
{{yield (hash Item=(component this.itemComponent item=item))}}
|
||||
{{else}}
|
||||
{{#if @collection.fetchedOnce}}
|
||||
{{yield (hash EmptyState=this.emptyStateComponent)}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
<div {{this.loadMore}}>
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<ConditionalLoadingSpinner @condition={{@collection.loading}} />
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import Component from "@glimmer/component";
|
||||
|
||||
export default class Item extends Component {
|
||||
<template>
|
||||
{{yield @item}}
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { hash } from "@ember/helper";
|
||||
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 OpenDrawerButton from "./open-drawer-button";
|
||||
import ThreadSettingsButton from "./thread-settings-button";
|
||||
import ThreadTrackingDropdown from "./thread-tracking-dropdown";
|
||||
import ThreadsListButton from "./threads-list-button";
|
||||
import ToggleDrawerButton from "./toggle-drawer-button";
|
||||
|
||||
export default class ChatNavbarActions extends Component {
|
||||
get openDrawerButtonComponent() {
|
||||
return OpenDrawerButton;
|
||||
}
|
||||
|
||||
get newChannelButtonComponent() {
|
||||
return NewChannelButton;
|
||||
}
|
||||
|
||||
get threadTrackingDropdownComponent() {
|
||||
return ThreadTrackingDropdown;
|
||||
}
|
||||
|
||||
get closeThreadButtonComponent() {
|
||||
return CloseThreadButton;
|
||||
}
|
||||
|
||||
get closeThreadsButtonComponent() {
|
||||
return CloseThreadsButton;
|
||||
}
|
||||
|
||||
get threadSettingsButtonComponent() {
|
||||
return ThreadSettingsButton;
|
||||
}
|
||||
|
||||
get threadsListButtonComponent() {
|
||||
return ThreadsListButton;
|
||||
}
|
||||
|
||||
get closeDrawerButtonComponent() {
|
||||
return CloseDrawerButton;
|
||||
}
|
||||
|
||||
get toggleDrawerButtonComponent() {
|
||||
return ToggleDrawerButton;
|
||||
}
|
||||
|
||||
get chatNavbarFullPageButtonComponent() {
|
||||
return FullPageButton;
|
||||
}
|
||||
|
||||
<template>
|
||||
<nav class="c-navbar__actions">
|
||||
{{yield
|
||||
(hash
|
||||
OpenDrawerButton=this.openDrawerButtonComponent
|
||||
NewChannelButton=this.newChannelButtonComponent
|
||||
ThreadTrackingDropdown=this.threadTrackingDropdownComponent
|
||||
CloseThreadButton=this.closeThreadButtonComponent
|
||||
CloseThreadsButton=this.closeThreadsButtonComponent
|
||||
ThreadSettingsButton=this.threadSettingsButtonComponent
|
||||
ThreadsListButton=this.threadsListButtonComponent
|
||||
CloseDrawerButton=this.closeDrawerButtonComponent
|
||||
ToggleDrawerButton=this.toggleDrawerButtonComponent
|
||||
FullPageButton=this.chatNavbarFullPageButtonComponent
|
||||
)
|
||||
}}
|
||||
</nav>
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default class ChatNavbarBackButton extends Component {
|
||||
get icon() {
|
||||
return this.args.icon ?? "chevron-left";
|
||||
}
|
||||
|
||||
get title() {
|
||||
return this.args.title ?? I18n.t("chat.browse.back");
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if @routeModels}}
|
||||
<LinkTo
|
||||
@route={{@route}}
|
||||
@models={{@routeModels}}
|
||||
class="c-navbar__back-button no-text btn-flat btn"
|
||||
title={{this.title}}
|
||||
>
|
||||
{{#if (has-block)}}
|
||||
{{yield}}
|
||||
{{else}}
|
||||
{{icon this.icon}}
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo
|
||||
@route="chat"
|
||||
class="c-navbar__back-button no-text btn-flat btn"
|
||||
title={{this.title}}
|
||||
>
|
||||
{{#if (has-block)}}
|
||||
{{yield}}
|
||||
{{else}}
|
||||
{{icon this.icon}}
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import ChannelTitle from "discourse/plugins/chat/discourse/components/channel-title";
|
||||
|
||||
export default class ChatNavbarChannelTitle extends Component {
|
||||
<template>
|
||||
{{#if @channel}}
|
||||
<LinkTo
|
||||
@route="chat.channel.info.members"
|
||||
@models={{@channel.routeModels}}
|
||||
class="c-navbar__channel-title"
|
||||
>
|
||||
<ChannelTitle @channel={{@channel}} />
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import DButton from "discourse/components/d-button";
|
||||
|
||||
export default class ChatNavbarCloseDrawerButton extends Component {
|
||||
@service chat;
|
||||
@service chatStateManager;
|
||||
|
||||
@action
|
||||
closeDrawer() {
|
||||
this.chatStateManager.didCloseDrawer();
|
||||
this.chat.activeChannel = null;
|
||||
}
|
||||
|
||||
<template>
|
||||
<DButton
|
||||
@icon="times"
|
||||
@action={{this.closeDrawer}}
|
||||
@title="chat.close"
|
||||
class="btn-flat no-text c-navbar__close-drawer-button"
|
||||
/>
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
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 "discourse-common/helpers/i18n";
|
||||
|
||||
export default class ChatNavbarCloseThreadButton extends Component {
|
||||
@service site;
|
||||
|
||||
<template>
|
||||
{{#if this.site.desktopView}}
|
||||
<LinkTo
|
||||
class="c-navbar__close-thread-button btn-flat btn btn-icon no-text"
|
||||
@route="chat.channel"
|
||||
@models={{@thread.channel.routeModels}}
|
||||
title={{i18n "chat.thread.close"}}
|
||||
>
|
||||
{{icon "times"}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
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 ChatNavbarCloseThreadsButton extends Component {
|
||||
@service site;
|
||||
|
||||
closeButtonTitle = I18n.t("chat.thread.close");
|
||||
|
||||
<template>
|
||||
{{#if this.site.desktopView}}
|
||||
<LinkTo
|
||||
class="c-navbar__close-threads-button btn-flat btn btn-icon no-text"
|
||||
@route="chat.channel"
|
||||
@models={{@channel.routeModels}}
|
||||
title={{this.closeButtonTitle}}
|
||||
>
|
||||
{{icon "times"}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { next } from "@ember/runloop";
|
||||
import { inject as service } from "@ember/service";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
|
||||
export default class ChatNavbarFullPageButton extends Component {
|
||||
@service chat;
|
||||
@service chatStateManager;
|
||||
|
||||
@action
|
||||
async openInFullPage() {
|
||||
this.chatStateManager.storeAppURL();
|
||||
this.chatStateManager.prefersFullPage();
|
||||
this.chat.activeChannel = null;
|
||||
|
||||
await new Promise((resolve) => next(resolve));
|
||||
|
||||
DiscourseURL.routeTo(this.chatStateManager.lastKnownChatURL);
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.chatStateManager.isDrawerExpanded}}
|
||||
<DButton
|
||||
@icon="discourse-expand"
|
||||
class="btn-flat no-text c-navbar__full-page-button"
|
||||
@title="chat.open_full_page"
|
||||
@action={{this.openInFullPage}}
|
||||
/>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { hash } from "@ember/helper";
|
||||
import { on } from "@ember/modifier";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import noop from "discourse/helpers/noop";
|
||||
import Actions from "./actions";
|
||||
import BackButton from "./back-button";
|
||||
import ChannelTitle from "./channel-title";
|
||||
import Title from "./title";
|
||||
|
||||
export default class ChatNavbar extends Component {
|
||||
get buttonComponent() {
|
||||
return BackButton;
|
||||
}
|
||||
|
||||
get titleComponent() {
|
||||
return Title;
|
||||
}
|
||||
|
||||
get actionsComponent() {
|
||||
return Actions;
|
||||
}
|
||||
|
||||
get channelTitleComponent() {
|
||||
return ChannelTitle;
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable no-invalid-interactive }}
|
||||
<div
|
||||
class={{concatClass "c-navbar-container" (if @onClick "-clickable")}}
|
||||
{{on "click" (if @onClick @onClick (noop))}}
|
||||
>
|
||||
<nav class="c-navbar">
|
||||
{{yield
|
||||
(hash
|
||||
BackButton=this.buttonComponent
|
||||
ChannelTitle=this.channelTitleComponent
|
||||
Title=this.titleComponent
|
||||
Actions=this.actionsComponent
|
||||
)
|
||||
}}
|
||||
</nav>
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
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 concatClass from "discourse/helpers/concat-class";
|
||||
import CreateChannelModal from "discourse/plugins/chat/discourse/components/chat/modal/create-channel";
|
||||
|
||||
export default class ChatNavbarNewChannelButton extends Component {
|
||||
@service chatStateManager;
|
||||
@service currentUser;
|
||||
@service modal;
|
||||
@service site;
|
||||
|
||||
@action
|
||||
createChannel() {
|
||||
this.modal.show(CreateChannelModal);
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.currentUser.staff}}
|
||||
<DButton
|
||||
@action={{this.createChannel}}
|
||||
@icon="plus"
|
||||
@label={{if this.site.desktopView "chat.create_channel.title"}}
|
||||
class={{concatClass
|
||||
"c-navbar__new-channel-button"
|
||||
(if this.site.mobileView "btn-flat")
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
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 DiscourseURL from "discourse/lib/url";
|
||||
|
||||
export default class ChatNavbarOpenDrawerButton extends Component {
|
||||
@service chatStateManager;
|
||||
@service site;
|
||||
|
||||
@action
|
||||
async openDrawer() {
|
||||
this.chatStateManager.prefersDrawer();
|
||||
|
||||
DiscourseURL.routeTo(this.chatStateManager.lastKnownAppURL).then(() => {
|
||||
DiscourseURL.routeTo(this.chatStateManager.lastKnownChatURL);
|
||||
});
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.site.desktopView}}
|
||||
<DButton
|
||||
@icon="discourse-compress"
|
||||
@title="chat.close_full_page"
|
||||
class="c-navbar__open-drawer-button btn-flat"
|
||||
@action={{this.openDrawer}}
|
||||
/>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import Component from "@glimmer/component";
|
||||
import SubTitle from "./sub-title";
|
||||
|
||||
export default class ChatNavbarSubTitle extends Component {
|
||||
get subTitleComponent() {
|
||||
return SubTitle;
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="c-navbar__sub-title">
|
||||
{{#if (has-block)}}
|
||||
{{yield}}
|
||||
{{else}}
|
||||
{{@title}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
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 ThreadSettingsModal from "discourse/plugins/chat/discourse/components/chat/modal/thread-settings";
|
||||
|
||||
export default class ChatNavbarThreadSettingsButton extends Component {
|
||||
@service currentUser;
|
||||
@service modal;
|
||||
|
||||
get canChangeThreadSettings() {
|
||||
if (!this.args.thread) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
this.currentUser.staff ||
|
||||
this.currentUser.id === this.args.thread.originalMessage.user.id
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
openThreadSettings() {
|
||||
this.modal.show(ThreadSettingsModal, { model: this.args.thread });
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.canChangeThreadSettings}}
|
||||
<DButton
|
||||
@action={{this.openThreadSettings}}
|
||||
@icon="cog"
|
||||
@title="chat.thread.settings"
|
||||
class="btn-flat c-navbar__thread-settings-button"
|
||||
/>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { NotificationLevels } from "discourse/lib/notification-levels";
|
||||
import ThreadTrackingDropdown from "discourse/plugins/chat/discourse/components/chat-thread-tracking-dropdown";
|
||||
import UserChatThreadMembership from "discourse/plugins/chat/discourse/models/user-chat-thread-membership";
|
||||
|
||||
export default class ChatNavbarThreadTrackingDropdown extends Component {
|
||||
@service chatApi;
|
||||
|
||||
@tracked persistedNotificationLevel = true;
|
||||
|
||||
get threadNotificationLevel() {
|
||||
return this.membership?.notificationLevel || NotificationLevels.REGULAR;
|
||||
}
|
||||
|
||||
get membership() {
|
||||
return this.args.thread.currentUserMembership;
|
||||
}
|
||||
|
||||
@action
|
||||
async updateThreadNotificationLevel(newNotificationLevel) {
|
||||
this.persistedNotificationLevel = false;
|
||||
|
||||
let currentNotificationLevel;
|
||||
|
||||
if (this.membership) {
|
||||
currentNotificationLevel = this.membership.notificationLevel;
|
||||
this.membership.notificationLevel = newNotificationLevel;
|
||||
} else {
|
||||
this.args.thread.currentUserMembership = UserChatThreadMembership.create({
|
||||
notification_level: newNotificationLevel,
|
||||
last_read_message_id: null,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const response =
|
||||
await this.chatApi.updateCurrentUserThreadNotificationsSettings(
|
||||
this.args.thread.channel.id,
|
||||
this.args.thread.id,
|
||||
{ notificationLevel: newNotificationLevel }
|
||||
);
|
||||
this.membership.last_read_message_id =
|
||||
response.membership.last_read_message_id;
|
||||
this.persistedNotificationLevel = true;
|
||||
} catch (error) {
|
||||
this.membership.notificationLevel = currentNotificationLevel;
|
||||
popupAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
<ThreadTrackingDropdown
|
||||
@value={{this.threadNotificationLevel}}
|
||||
@onChange={{this.updateThreadNotificationLevel}}
|
||||
@class={{concatClass
|
||||
"c-navbar__thread-tracking-dropdown"
|
||||
(if this.persistedNotificationLevel "-persisted")
|
||||
}}
|
||||
/>
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import { inject as service } from "@ember/service";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import I18n from "I18n";
|
||||
import ThreadHeaderUnreadIndicator from "discourse/plugins/chat/discourse/components/chat/thread/header-unread-indicator";
|
||||
|
||||
export default class ChatNavbarThreadsListButton extends Component {
|
||||
@service router;
|
||||
|
||||
threadsListLabel = I18n.t("chat.threads.list");
|
||||
|
||||
get showThreadsListButton() {
|
||||
return (
|
||||
this.args.channel?.threadingEnabled &&
|
||||
this.router.currentRoute.name !== "chat.channel.threads" &&
|
||||
this.router.currentRoute.name !== "chat.channel.thread" &&
|
||||
this.router.currentRoute.name !== "chat.channel.thread.index"
|
||||
);
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.showThreadsListButton}}
|
||||
<LinkTo
|
||||
@route="chat.channel.threads"
|
||||
@models={{@channel.routeModels}}
|
||||
title={{this.threadsListLabel}}
|
||||
class={{concatClass
|
||||
"c-navbar__threads-list-button"
|
||||
"btn"
|
||||
"no-text"
|
||||
"btn-flat"
|
||||
(if @channel.threadsManager.unreadThreadCount "has-unreads")
|
||||
}}
|
||||
>
|
||||
{{icon "discourse-threads"}}
|
||||
<ThreadHeaderUnreadIndicator @channel={{@channel}} />
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { hash } from "@ember/helper";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import SubTitle from "./sub-title";
|
||||
|
||||
export default class ChatNavbarTitle extends Component {
|
||||
get subTitleComponent() {
|
||||
return SubTitle;
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="c-navbar__title">
|
||||
{{#if (has-block)}}
|
||||
{{#if @icon}}
|
||||
{{icon @icon}}
|
||||
{{/if}}
|
||||
{{@title}}
|
||||
{{yield (hash SubTitle=this.subTitleComponent)}}
|
||||
{{else}}
|
||||
{{#if @icon}}
|
||||
{{icon @icon}}
|
||||
{{/if}}
|
||||
{{@title}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -2,7 +2,8 @@ import Component from "@glimmer/component";
|
|||
import { inject as service } from "@ember/service";
|
||||
import DButton from "discourse/components/d-button";
|
||||
|
||||
export default class ChatDrawerHeaderToggleExpandButton extends Component {
|
||||
export default class ChatNavbarToggleDrawerButton extends Component {
|
||||
@service chat;
|
||||
@service chatStateManager;
|
||||
|
||||
<template>
|
||||
|
@ -12,13 +13,13 @@ export default class ChatDrawerHeaderToggleExpandButton extends Component {
|
|||
"angle-double-down"
|
||||
"angle-double-up"
|
||||
}}
|
||||
@action={{@toggleExpand}}
|
||||
@action={{this.chat.toggleDrawer}}
|
||||
@title={{if
|
||||
this.chatStateManager.isDrawerExpanded
|
||||
"chat.collapse"
|
||||
"chat.expand"
|
||||
}}
|
||||
class="btn-flat btn-link chat-drawer-header__expand-btn"
|
||||
class="btn-flat no-text c-navbar__toggle-drawer-button"
|
||||
/>
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
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 { inject as 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.attrs.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 |action|>
|
||||
<action.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>
|
||||
}
|
|
@ -13,11 +13,11 @@ import icon from "discourse-common/helpers/d-icon";
|
|||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import I18n from "discourse-i18n";
|
||||
import MessageCreator from "discourse/plugins/chat/discourse/components/chat/message-creator";
|
||||
import { MODES } from "discourse/plugins/chat/discourse/components/chat/message-creator/constants";
|
||||
import ChatUserInfo from "discourse/plugins/chat/discourse/components/chat-user-info";
|
||||
import DcFilterInput from "discourse/plugins/chat/discourse/components/dc-filter-input";
|
||||
import { MODES } from "./chat/message-creator/constants";
|
||||
|
||||
export default class ChatChannelMembers extends Component {
|
||||
export default class ChatRouteChannelInfoMembers extends Component {
|
||||
@service appEvents;
|
||||
@service chatApi;
|
||||
@service modal;
|
|
@ -28,7 +28,7 @@ const NOTIFICATION_LEVELS = [
|
|||
{ name: I18n.t("chat.notification_levels.always"), value: "always" },
|
||||
];
|
||||
|
||||
export default class ChatAboutScreen extends Component {
|
||||
export default class ChatRouteChannelInfoSettings extends Component {
|
||||
@service chatApi;
|
||||
@service chatGuardian;
|
||||
@service chatChannelsManager;
|
|
@ -0,0 +1,91 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import { inject as service } from "@ember/service";
|
||||
import I18n from "discourse-i18n";
|
||||
import ChatModalEditChannelName from "discourse/plugins/chat/discourse/components/chat/modal/edit-channel-name";
|
||||
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
|
||||
import ChatChannelStatus from "discourse/plugins/chat/discourse/components/chat-channel-status";
|
||||
|
||||
export default class ChatRoutesChannelInfo extends Component {
|
||||
@service chatChannelInfoRouteOriginManager;
|
||||
@service site;
|
||||
@service modal;
|
||||
@service chatGuardian;
|
||||
|
||||
membersLabel = I18n.t("chat.channel_info.tabs.members");
|
||||
settingsLabel = I18n.t("chat.channel_info.tabs.settings");
|
||||
backToChannelLabel = I18n.t("chat.channel_info.back_to_all_channel");
|
||||
backToAllChannelsLabel = I18n.t("chat.channel_info.back_to_channel");
|
||||
|
||||
get showTabs() {
|
||||
return this.site.desktopView && this.args.channel.isOpen;
|
||||
}
|
||||
|
||||
get canEditChannel() {
|
||||
return (
|
||||
this.chatGuardian.canEditChatChannel() &&
|
||||
(this.args.channel.isCategoryChannel ||
|
||||
(this.args.channel.isDirectMessageChannel &&
|
||||
this.args.channel.chatable.group))
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
editChannelTitle() {
|
||||
return this.modal.show(ChatModalEditChannelName, {
|
||||
model: this.args.channel,
|
||||
});
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="c-routes-channel-info">
|
||||
<Navbar as |navbar|>
|
||||
{{#if this.chatChannelInfoRouteOriginManager.isBrowse}}
|
||||
<navbar.BackButton
|
||||
@route="chat.browse"
|
||||
@title={{this.backToAllChannelsLabel}}
|
||||
/>
|
||||
{{else}}
|
||||
<navbar.BackButton
|
||||
@route="chat.channel"
|
||||
@routeModels={{@channel.routeModels}}
|
||||
@title={{this.backToChannelLabel}}
|
||||
/>
|
||||
{{/if}}
|
||||
<navbar.ChannelTitle @channel={{@channel}} />
|
||||
</Navbar>
|
||||
|
||||
<ChatChannelStatus @channel={{@channel}} />
|
||||
|
||||
<div class="chat-channel-info">
|
||||
{{#if this.showTabs}}
|
||||
<nav class="chat-channel-info__nav">
|
||||
<ul class="nav nav-pills">
|
||||
<li>
|
||||
<LinkTo
|
||||
@route="chat.channel.info.settings"
|
||||
@model={{@channel}}
|
||||
@replace={{true}}
|
||||
>
|
||||
{{this.settingsLabel}}
|
||||
</LinkTo>
|
||||
</li>
|
||||
<li>
|
||||
<LinkTo
|
||||
@route="chat.channel.info.members"
|
||||
@model={{@channel}}
|
||||
@replace={{true}}
|
||||
>
|
||||
{{this.membersLabel}}
|
||||
</LinkTo>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{{/if}}
|
||||
|
||||
{{outlet}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { array } from "@ember/helper";
|
||||
import ThreadHeader from "discourse/plugins/chat/discourse/components/chat/thread/header";
|
||||
import Thread from "discourse/plugins/chat/discourse/components/chat-thread";
|
||||
|
||||
export default class ChatRoutesChannelThread extends Component {
|
||||
<template>
|
||||
<div class="c-routes-channel-thread">
|
||||
{{#each (array @thread) as |thread|}}
|
||||
<ThreadHeader @thread={{thread}} />
|
||||
|
||||
<Thread
|
||||
@thread={{thread}}
|
||||
@targetMessageId={{@targetMessageId}}
|
||||
@includeHeader={{true}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import Component from "@glimmer/component";
|
||||
import ChatThreadListHeader from "discourse/plugins/chat/discourse/components/chat/thread-list/header";
|
||||
import ChatThreadList from "discourse/plugins/chat/discourse/components/chat-thread-list";
|
||||
|
||||
export default class ChatRoutesChannelThreads extends Component {
|
||||
<template>
|
||||
<div class="c-routes-channel-threads">
|
||||
<ChatThreadListHeader @channel={{@channel}} />
|
||||
<ChatThreadList @channel={{@channel}} @includeHeader={{true}} />
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { inject as service } from "@ember/service";
|
||||
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
|
||||
import SidePanel from "discourse/plugins/chat/discourse/components/chat-side-panel";
|
||||
import FullPageChat from "discourse/plugins/chat/discourse/components/full-page-chat";
|
||||
|
||||
export default class ChatRoutesChannel extends Component {
|
||||
@service site;
|
||||
|
||||
<template>
|
||||
<div class="c-routes-channel">
|
||||
<Navbar as |navbar|>
|
||||
{{#if this.site.mobileView}}
|
||||
<navbar.BackButton />
|
||||
{{/if}}
|
||||
<navbar.ChannelTitle @channel={{@channel}} />
|
||||
<navbar.Actions as |action|>
|
||||
<action.OpenDrawerButton />
|
||||
<action.ThreadsListButton @channel={{@channel}} />
|
||||
</navbar.Actions>
|
||||
</Navbar>
|
||||
|
||||
<FullPageChat
|
||||
@channel={{@channel}}
|
||||
@targetMessageId={{@targetMessageId}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<SidePanel>
|
||||
{{outlet}}
|
||||
</SidePanel>
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import Component from "@glimmer/component";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
|
||||
import UserThreads from "discourse/plugins/chat/discourse/components/user-threads";
|
||||
|
||||
export default class ChatRoutesThreads extends Component {
|
||||
<template>
|
||||
<div class="c-routes-threads">
|
||||
<Navbar as |navbar|>
|
||||
<navbar.BackButton />
|
||||
<navbar.Title
|
||||
@title={{i18n "chat.my_threads.title"}}
|
||||
@icon="discourse-threads"
|
||||
/>
|
||||
|
||||
<navbar.Actions as |action|>
|
||||
<action.OpenDrawerButton />
|
||||
</navbar.Actions>
|
||||
</Navbar>
|
||||
|
||||
<UserThreads />
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -1,70 +1,37 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import { inject as service } from "@ember/service";
|
||||
import replaceEmoji from "discourse/helpers/replace-emoji";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import I18n from "discourse-i18n";
|
||||
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
|
||||
|
||||
export default class ChatThreadListHeader extends Component {
|
||||
@service router;
|
||||
@service site;
|
||||
|
||||
threadListTitle = I18n.t("chat.threads.list");
|
||||
closeButtonTitle = I18n.t("chat.thread.close");
|
||||
showCloseButton = !this.site.mobileView;
|
||||
|
||||
get showBackButton() {
|
||||
return this.args.channel && this.site.mobileView;
|
||||
}
|
||||
|
||||
get backButton() {
|
||||
return {
|
||||
route: "chat.channel.index",
|
||||
models: this.args.channel.routeModels,
|
||||
title: I18n.t("chat.return_to_channel"),
|
||||
};
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="chat-thread-list-header">
|
||||
<div class="chat-thread-header__left-buttons">
|
||||
{{#if this.showBackButton}}
|
||||
<LinkTo
|
||||
class="chat-thread__back-to-previous-route btn-flat btn btn-icon no-text"
|
||||
@route={{this.backButton.route}}
|
||||
@models={{this.backButton.models}}
|
||||
title={{this.backButton.title}}
|
||||
>
|
||||
{{icon "chevron-left"}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="chat-thread-list-header__label">
|
||||
<span>
|
||||
{{icon "discourse-threads"}}
|
||||
{{replaceEmoji this.threadListTitle}}
|
||||
</span>
|
||||
|
||||
{{#if this.site.mobileView}}
|
||||
<div class="chat-thread-list-header__label-channel">
|
||||
{{replaceEmoji @channel.title}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if this.showCloseButton}}
|
||||
<div class="chat-thread-header__buttons">
|
||||
<LinkTo
|
||||
class="chat-thread__close btn-flat btn btn-icon no-text"
|
||||
<Navbar as |navbar|>
|
||||
<navbar.BackButton
|
||||
@route="chat.channel"
|
||||
@models={{@channel.routeModels}}
|
||||
title={{this.closeButtonTitle}}
|
||||
@routeModels={{@channel.routeModels}}
|
||||
@title={{i18n "chat.return_to_channel"}}
|
||||
/>
|
||||
|
||||
<navbar.Title
|
||||
@title={{replaceEmoji this.threadListTitle}}
|
||||
@icon="discourse-threads"
|
||||
as |title|
|
||||
>
|
||||
{{icon "times"}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
{{#if this.site.mobileView}}
|
||||
<title.SubTitle @title={{replaceEmoji @channel.title}} />
|
||||
{{/if}}
|
||||
</div>
|
||||
</navbar.Title>
|
||||
|
||||
<navbar.Actions as |action|>
|
||||
<action.CloseThreadsButton @channel={{@channel}} />
|
||||
</navbar.Actions>
|
||||
</Navbar>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@ export default class ChatThreadListItem extends Component {
|
|||
</div>
|
||||
|
||||
<div class="chat-thread-list-item__metadata">
|
||||
|
||||
<div class="chat-thread-list-item__members">
|
||||
<ChatUserAvatar
|
||||
@user={{@thread.originalMessage.user}}
|
||||
|
@ -72,7 +71,6 @@ export default class ChatThreadListItem extends Component {
|
|||
}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,32 +1,15 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { action } from "@ember/object";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import { inject as service } from "@ember/service";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import replaceEmoji from "discourse/helpers/replace-emoji";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { NotificationLevels } from "discourse/lib/notification-levels";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import I18n from "discourse-i18n";
|
||||
import ChatModalThreadSettings from "discourse/plugins/chat/discourse/components/chat/modal/thread-settings";
|
||||
import Navbar from "discourse/plugins/chat/discourse/components/chat/navbar";
|
||||
import ChatThreadHeaderUnreadIndicator from "discourse/plugins/chat/discourse/components/chat/thread/header-unread-indicator";
|
||||
import UserChatThreadMembership from "discourse/plugins/chat/discourse/models/user-chat-thread-membership";
|
||||
import ThreadNotificationsButton from "discourse/plugins/chat/select-kit/addons/components/thread-notifications-button";
|
||||
|
||||
export default class ChatThreadHeader extends Component {
|
||||
@service currentUser;
|
||||
@service chatApi;
|
||||
@service router;
|
||||
@service chatStateManager;
|
||||
@service chatHistory;
|
||||
@service site;
|
||||
@service modal;
|
||||
|
||||
@tracked persistedNotificationLevel = true;
|
||||
|
||||
closeThreadTitle = I18n.t("chat.thread.close");
|
||||
|
||||
get backLink() {
|
||||
const prevPage = this.chatHistory.previousRoute?.name;
|
||||
|
@ -35,15 +18,15 @@ export default class ChatThreadHeader extends Component {
|
|||
if (prevPage === "chat.channel.threads") {
|
||||
route = "chat.channel.threads";
|
||||
title = I18n.t("chat.return_to_threads_list");
|
||||
models = this.args.channel.routeModels;
|
||||
models = this.channel?.routeModels;
|
||||
} else if (prevPage === "chat.channel.index" && !this.site.mobileView) {
|
||||
route = "chat.channel.threads";
|
||||
title = I18n.t("chat.return_to_threads_list");
|
||||
models = this.args.channel.routeModels;
|
||||
models = this.channel?.routeModels;
|
||||
} else if (!this.currentUser.isInDoNotDisturb() && this.unreadCount > 0) {
|
||||
route = "chat.channel.threads";
|
||||
title = I18n.t("chat.return_to_threads_list");
|
||||
models = this.args.channel.routeModels;
|
||||
models = this.channel?.routeModels;
|
||||
} else if (prevPage === "chat.threads") {
|
||||
route = "chat.threads";
|
||||
title = I18n.t("chat.my_threads.title");
|
||||
|
@ -51,29 +34,14 @@ export default class ChatThreadHeader extends Component {
|
|||
} else {
|
||||
route = "chat.channel.index";
|
||||
title = I18n.t("chat.return_to_channel");
|
||||
models = this.args.channel.routeModels;
|
||||
models = this.channel?.routeModels;
|
||||
}
|
||||
|
||||
return { route, models, title };
|
||||
}
|
||||
|
||||
get canChangeThreadSettings() {
|
||||
if (!this.args.thread) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
this.currentUser.staff ||
|
||||
this.currentUser.id === this.args.thread.originalMessage.user.id
|
||||
);
|
||||
}
|
||||
|
||||
get threadNotificationLevel() {
|
||||
return this.membership?.notificationLevel || NotificationLevels.REGULAR;
|
||||
}
|
||||
|
||||
get membership() {
|
||||
return this.args.thread.currentUserMembership;
|
||||
get channel() {
|
||||
return this.args.thread?.channel;
|
||||
}
|
||||
|
||||
get headerTitle() {
|
||||
|
@ -81,97 +49,28 @@ export default class ChatThreadHeader extends Component {
|
|||
}
|
||||
|
||||
get unreadCount() {
|
||||
return this.args.channel.threadsManager.unreadThreadCount;
|
||||
}
|
||||
|
||||
@action
|
||||
openThreadSettings() {
|
||||
this.modal.show(ChatModalThreadSettings, { model: this.args.thread });
|
||||
}
|
||||
|
||||
@action
|
||||
updateThreadNotificationLevel(newNotificationLevel) {
|
||||
this.persistedNotificationLevel = false;
|
||||
|
||||
let currentNotificationLevel;
|
||||
|
||||
if (this.membership) {
|
||||
currentNotificationLevel = this.membership.notificationLevel;
|
||||
this.membership.notificationLevel = newNotificationLevel;
|
||||
} else {
|
||||
this.args.thread.currentUserMembership = UserChatThreadMembership.create({
|
||||
notification_level: newNotificationLevel,
|
||||
last_read_message_id: null,
|
||||
});
|
||||
}
|
||||
|
||||
return this.chatApi
|
||||
.updateCurrentUserThreadNotificationsSettings(
|
||||
this.args.thread.channel.id,
|
||||
this.args.thread.id,
|
||||
{ notificationLevel: newNotificationLevel }
|
||||
)
|
||||
.then((response) => {
|
||||
this.membership.last_read_message_id =
|
||||
response.membership.last_read_message_id;
|
||||
|
||||
this.persistedNotificationLevel = true;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.membership.notificationLevel = currentNotificationLevel;
|
||||
popupAjaxError(err);
|
||||
});
|
||||
return this.channel?.threadsManager?.unreadThreadCount;
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="chat-thread-header">
|
||||
<div class="chat-thread-header__left-buttons">
|
||||
<Navbar as |navbar|>
|
||||
{{#if @thread}}
|
||||
<LinkTo
|
||||
class="chat-thread__back-to-previous-route btn-flat btn btn-icon no-text"
|
||||
<navbar.BackButton
|
||||
@route={{this.backLink.route}}
|
||||
@models={{this.backLink.models}}
|
||||
title={{this.backLink.title}}
|
||||
@routeModels={{this.backLink.models}}
|
||||
@title={{this.backLink.title}}
|
||||
>
|
||||
<ChatThreadHeaderUnreadIndicator @channel={{@thread.channel}} />
|
||||
<ChatThreadHeaderUnreadIndicator @channel={{this.channel}} />
|
||||
{{icon "chevron-left"}}
|
||||
</LinkTo>
|
||||
</navbar.BackButton>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<span class="chat-thread-header__label overflow-ellipsis">
|
||||
{{replaceEmoji this.headerTitle}}
|
||||
</span>
|
||||
|
||||
<div
|
||||
class={{concatClass
|
||||
"chat-thread-header__buttons"
|
||||
(if this.persistedNotificationLevel "-persisted")
|
||||
}}
|
||||
>
|
||||
<ThreadNotificationsButton
|
||||
@value={{this.threadNotificationLevel}}
|
||||
@onChange={{this.updateThreadNotificationLevel}}
|
||||
/>
|
||||
{{#if this.canChangeThreadSettings}}
|
||||
<DButton
|
||||
@action={{this.openThreadSettings}}
|
||||
@icon="cog"
|
||||
@title="chat.thread.settings"
|
||||
class="btn-flat chat-thread-header__settings"
|
||||
/>
|
||||
{{/if}}
|
||||
{{#unless this.site.mobileView}}
|
||||
<LinkTo
|
||||
class="chat-thread__close btn-flat btn btn-icon no-text"
|
||||
@route="chat.channel"
|
||||
@models={{@thread.channel.routeModels}}
|
||||
title={{this.closeThreadTitle}}
|
||||
>
|
||||
{{icon "times"}}
|
||||
</LinkTo>
|
||||
{{/unless}}
|
||||
</div>
|
||||
</div>
|
||||
<navbar.Title @title={{replaceEmoji this.headerTitle}} />
|
||||
<navbar.Actions as |action|>
|
||||
<action.ThreadTrackingDropdown @thread={{@thread}} />
|
||||
<action.ThreadSettingsButton @thread={{@thread}} />
|
||||
<action.CloseThreadButton @thread={{@thread}} />
|
||||
</navbar.Actions>
|
||||
</Navbar>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import I18n from "I18n";
|
||||
import ThreadHeaderUnreadIndicator from "discourse/plugins/chat/discourse/components/chat/thread/header-unread-indicator";
|
||||
|
||||
export default class ThreadsListButton extends Component {
|
||||
threadsListLabel = I18n.t("chat.threads.list");
|
||||
|
||||
<template>
|
||||
<LinkTo
|
||||
@route="chat.channel.threads"
|
||||
@models={{@channel.routeModels}}
|
||||
title={{this.threadsListLabel}}
|
||||
class={{concatClass
|
||||
"chat-threads-list-button"
|
||||
"btn"
|
||||
"btn-flat"
|
||||
(if @channel.threadsManager.unreadThreadCount "has-unreads")
|
||||
}}
|
||||
>
|
||||
{{icon "discourse-threads"}}
|
||||
|
||||
<ThreadHeaderUnreadIndicator @channel={{@channel}} />
|
||||
</LinkTo>
|
||||
</template>
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
import Component from "@glimmer/component";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import Navbar from "discourse/plugins/chat/discourse/components/navbar";
|
||||
import UserThreads from "discourse/plugins/chat/discourse/components/user-threads";
|
||||
|
||||
export default class ChatThreads extends Component {
|
||||
<template>
|
||||
<div class="chat-threads">
|
||||
<Navbar>
|
||||
<:current>
|
||||
{{icon "discourse-threads"}}
|
||||
{{i18n "chat.my_threads.title"}}
|
||||
</:current>
|
||||
</Navbar>
|
||||
|
||||
<UserThreads />
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
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 DiscourseURL from "discourse/lib/url";
|
||||
|
||||
export default class ChatNavbar extends Component {
|
||||
@service chatStateManager;
|
||||
|
||||
@action
|
||||
async closeFullScreen() {
|
||||
this.chatStateManager.prefersDrawer();
|
||||
|
||||
try {
|
||||
await DiscourseURL.routeTo(this.chatStateManager.lastKnownAppURL);
|
||||
await DiscourseURL.routeTo(this.chatStateManager.lastKnownChatURL);
|
||||
} catch (error) {
|
||||
await DiscourseURL.routeTo("/");
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="chat-navbar-container">
|
||||
<nav class="chat-navbar">
|
||||
{{#if (has-block "current")}}
|
||||
<span class="chat-navbar__current">
|
||||
{{yield to="current"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
<ul class="chat-navbar__right-actions">
|
||||
<li class="chat-navbar__right-action">
|
||||
<DButton
|
||||
@icon="discourse-compress"
|
||||
@title="chat.close_full_page"
|
||||
class="open-drawer-btn btn-flat"
|
||||
@action={{this.closeFullScreen}}
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -1,14 +1,9 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { cached } from "@glimmer/tracking";
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { modifier } from "ember-modifier";
|
||||
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
|
||||
import isElementInViewport from "discourse/lib/is-element-in-viewport";
|
||||
import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import ChannelTitle from "discourse/plugins/chat/discourse/components/channel-title";
|
||||
import List from "discourse/plugins/chat/discourse/components/chat/list";
|
||||
import ThreadIndicator from "discourse/plugins/chat/discourse/components/chat-message-thread-indicator";
|
||||
import ThreadTitle from "discourse/plugins/chat/discourse/components/thread-title";
|
||||
import ChatChannel from "discourse/plugins/chat/discourse/models/chat-channel";
|
||||
|
@ -17,45 +12,12 @@ import ChatThread from "discourse/plugins/chat/discourse/models/chat-thread";
|
|||
export default class UserThreads extends Component {
|
||||
@service chat;
|
||||
@service chatApi;
|
||||
@service router;
|
||||
|
||||
loadMore = modifier((element) => {
|
||||
this.intersectionObserver = new IntersectionObserver(this.loadThreads);
|
||||
this.intersectionObserver.observe(element);
|
||||
|
||||
return () => {
|
||||
this.intersectionObserver.disconnect();
|
||||
};
|
||||
});
|
||||
|
||||
fill = modifier((element) => {
|
||||
this.resizeObserver = new ResizeObserver(() => {
|
||||
if (isElementInViewport(element)) {
|
||||
this.loadThreads();
|
||||
}
|
||||
});
|
||||
|
||||
this.resizeObserver.observe(element);
|
||||
|
||||
return () => {
|
||||
this.resizeObserver.disconnect();
|
||||
};
|
||||
});
|
||||
|
||||
@cached
|
||||
get threadsCollection() {
|
||||
return this.chatApi.userThreads(this.handleLoadedThreads);
|
||||
}
|
||||
|
||||
@action
|
||||
loadThreads() {
|
||||
discourseDebounce(this, this.debouncedLoadThreads, INPUT_DELAY);
|
||||
}
|
||||
|
||||
async debouncedLoadThreads() {
|
||||
await this.threadsCollection.load({ limit: 10 });
|
||||
}
|
||||
|
||||
@bind
|
||||
handleLoadedThreads(result) {
|
||||
return result.threads.map((threadObject) => {
|
||||
|
@ -71,12 +33,15 @@ export default class UserThreads extends Component {
|
|||
}
|
||||
|
||||
<template>
|
||||
<div class="c-user-threads" {{this.fill}}>
|
||||
{{#each this.threadsCollection.items as |thread|}}
|
||||
<List
|
||||
@collection={{this.threadsCollection}}
|
||||
class="c-user-threads"
|
||||
as |list|
|
||||
>
|
||||
<list.Item as |thread|>
|
||||
<div class="c-user-thread" data-id={{thread.id}}>
|
||||
<ThreadTitle @thread={{thread}} />
|
||||
<ChannelTitle @channel={{thread.channel}} />
|
||||
|
||||
<ThreadIndicator
|
||||
@message={{thread.originalMessage}}
|
||||
@interactiveUser={{false}}
|
||||
|
@ -84,16 +49,7 @@ export default class UserThreads extends Component {
|
|||
tabindex="-1"
|
||||
/>
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
<div {{this.loadMore}}>
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<ConditionalLoadingSpinner
|
||||
@condition={{this.threadsCollection.loading}}
|
||||
/>
|
||||
|
||||
</div>
|
||||
</list.Item>
|
||||
</List>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -94,12 +94,16 @@ export default class ChatApi extends Service {
|
|||
*
|
||||
* this.chatApi.channels.then(channels => { ... })
|
||||
*/
|
||||
channels() {
|
||||
return new Collection(`${this.#basePath}/channels`, (response) => {
|
||||
channels(params = {}) {
|
||||
return new Collection(
|
||||
`${this.#basePath}/channels`,
|
||||
(response) => {
|
||||
return response.channels.map((channel) =>
|
||||
this.chatChannelsManager.store(channel)
|
||||
);
|
||||
});
|
||||
},
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import { tracked } from "@glimmer/tracking";
|
||||
import Service, { inject as service } from "@ember/service";
|
||||
import ChatDrawerChannel from "discourse/plugins/chat/discourse/components/chat-drawer/channel";
|
||||
import ChatDrawerChannelThreads from "discourse/plugins/chat/discourse/components/chat-drawer/channel-threads";
|
||||
import ChatDrawerIndex from "discourse/plugins/chat/discourse/components/chat-drawer/index";
|
||||
import ChatDrawerThread from "discourse/plugins/chat/discourse/components/chat-drawer/thread";
|
||||
import ChatDrawerThreads from "discourse/plugins/chat/discourse/components/chat-drawer/threads";
|
||||
import ChatDrawerRoutesChannel from "discourse/plugins/chat/discourse/components/chat/drawer-routes/channel";
|
||||
import ChatDrawerRoutesChannelThread from "discourse/plugins/chat/discourse/components/chat/drawer-routes/channel-thread";
|
||||
import ChatDrawerRoutesChannelThreads from "discourse/plugins/chat/discourse/components/chat/drawer-routes/channel-threads";
|
||||
import ChatDrawerRoutesChannels from "discourse/plugins/chat/discourse/components/chat/drawer-routes/channels";
|
||||
import ChatDrawerRoutesThreads from "discourse/plugins/chat/discourse/components/chat/drawer-routes/threads";
|
||||
|
||||
const ROUTES = {
|
||||
"chat.channel": { name: ChatDrawerChannel },
|
||||
"chat.channel": { name: ChatDrawerRoutesChannel },
|
||||
"chat.channel.thread": {
|
||||
name: ChatDrawerThread,
|
||||
name: ChatDrawerRoutesChannelThread,
|
||||
extractParams: (route) => {
|
||||
return {
|
||||
channelId: route.parent.params.channelId,
|
||||
|
@ -18,7 +18,7 @@ const ROUTES = {
|
|||
},
|
||||
},
|
||||
"chat.channel.thread.index": {
|
||||
name: ChatDrawerThread,
|
||||
name: ChatDrawerRoutesChannelThread,
|
||||
extractParams: (route) => {
|
||||
return {
|
||||
channelId: route.parent.params.channelId,
|
||||
|
@ -27,7 +27,7 @@ const ROUTES = {
|
|||
},
|
||||
},
|
||||
"chat.channel.thread.near-message": {
|
||||
name: ChatDrawerThread,
|
||||
name: ChatDrawerRoutesChannelThread,
|
||||
extractParams: (route) => {
|
||||
return {
|
||||
channelId: route.parent.parent.params.channelId,
|
||||
|
@ -37,7 +37,7 @@ const ROUTES = {
|
|||
},
|
||||
},
|
||||
"chat.channel.threads": {
|
||||
name: ChatDrawerChannelThreads,
|
||||
name: ChatDrawerRoutesChannelThreads,
|
||||
extractParams: (route) => {
|
||||
return {
|
||||
channelId: route.parent.params.channelId,
|
||||
|
@ -45,11 +45,11 @@ const ROUTES = {
|
|||
},
|
||||
},
|
||||
"chat.threads": {
|
||||
name: ChatDrawerThreads,
|
||||
name: ChatDrawerRoutesThreads,
|
||||
},
|
||||
chat: { name: ChatDrawerIndex },
|
||||
chat: { name: ChatDrawerRoutesChannels },
|
||||
"chat.channel.near-message": {
|
||||
name: ChatDrawerChannel,
|
||||
name: ChatDrawerRoutesChannel,
|
||||
extractParams: (route) => {
|
||||
return {
|
||||
channelId: route.parent.params.channelId,
|
||||
|
@ -58,7 +58,7 @@ const ROUTES = {
|
|||
},
|
||||
},
|
||||
"chat.channel-legacy": {
|
||||
name: ChatDrawerChannel,
|
||||
name: ChatDrawerRoutesChannel,
|
||||
extractParams: (route) => {
|
||||
return {
|
||||
channelId: route.params.channelId,
|
||||
|
@ -83,7 +83,7 @@ export default class ChatDrawerRouter extends Service {
|
|||
|
||||
this.drawerRoute = ROUTES[route.name];
|
||||
this.params = this.drawerRoute?.extractParams?.(route) || route.params;
|
||||
this.component = this.drawerRoute?.name || ChatDrawerIndex;
|
||||
this.component = this.drawerRoute?.name || ChatDrawerRoutesChannels;
|
||||
|
||||
this.drawerRoute.activate?.(route);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { tracked } from "@glimmer/tracking";
|
||||
import { computed } from "@ember/object";
|
||||
import { action, computed } from "@ember/object";
|
||||
import { and } from "@ember/object/computed";
|
||||
import { cancel, next } from "@ember/runloop";
|
||||
import Service, { inject as service } from "@ember/service";
|
||||
|
@ -414,4 +414,13 @@ export default class Chat extends Service {
|
|||
"Use the new chat API `api.registerChatComposerButton` instead of `chat.addToolbarButton`"
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
toggleDrawer() {
|
||||
this.chatStateManager.didToggleDrawer();
|
||||
this.appEvents.trigger(
|
||||
"chat:toggle-expand",
|
||||
this.chatStateManager.isDrawerExpanded
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
<ChatBrowseView @status="all" />
|
||||
<Chat::Routes::Browse @status="all" />
|
|
@ -1 +1 @@
|
|||
<ChatBrowseView @status="archived" />
|
||||
<Chat::Routes::Browse @status="archived" />
|
|
@ -1 +1 @@
|
|||
<ChatBrowseView @status="closed" />
|
||||
<Chat::Routes::Browse @status="closed" />
|
|
@ -1 +1 @@
|
|||
<ChatBrowseView @status="open" />
|
||||
<Chat::Routes::Browse @status="open" />
|
|
@ -1 +1 @@
|
|||
<ChatChannelMembers @channel={{this.model}} />
|
||||
<Chat::Routes::ChannelInfoMembers @channel={{this.model}} />
|
|
@ -1 +1 @@
|
|||
<ChatChannelSettings @channel={{this.model}} />
|
||||
<Chat::Routes::ChannelInfoSettings @channel={{this.model}} />
|
|
@ -1 +1 @@
|
|||
<ChatChannelInfo @channel={{this.model}} />
|
||||
<Chat::Routes::ChannelInfo @channel={{this.model}} />
|
|
@ -1,7 +1,4 @@
|
|||
{{#each (array this.model) as |thread|}}
|
||||
<ChatThread
|
||||
@thread={{thread}}
|
||||
<Chat::Routes::ChannelThread
|
||||
@thread={{this.model}}
|
||||
@targetMessageId={{this.targetMessageId}}
|
||||
@includeHeader={{true}}
|
||||
/>
|
||||
{{/each}}
|
||||
/>
|
|
@ -1 +1 @@
|
|||
<ChatThreadList @channel={{this.model}} @includeHeader={{true}} />
|
||||
<Chat::Routes::ChannelThreads @channel={{this.model}} />
|
|
@ -1,8 +1,4 @@
|
|||
<FullPageChat
|
||||
<Chat::Routes::Channel
|
||||
@channel={{this.model}}
|
||||
@targetMessageId={{this.targetMessageId}}
|
||||
/>
|
||||
|
||||
<ChatSidePanel>
|
||||
{{outlet}}
|
||||
</ChatSidePanel>
|
|
@ -1 +0,0 @@
|
|||
<ChatDraftChannelScreen />
|
|
@ -1 +1 @@
|
|||
<Chat::Threads />
|
||||
<Chat::Routes::Threads />
|
|
@ -3,7 +3,7 @@
|
|||
--full-page-border-radius: 12px;
|
||||
--full-page-sidebar-width: 275px;
|
||||
--channel-list-avatar-size: 30px;
|
||||
--chat-header-offset: 50px;
|
||||
--chat-header-offset: 46px;
|
||||
}
|
||||
|
||||
// Very specific hack to ensure the contextual menu (copy/paste/...) is
|
||||
|
@ -198,40 +198,9 @@ body.has-full-page-chat {
|
|||
grid-template-columns: var(--full-page-sidebar-width) 1fr;
|
||||
background: var(--d-content-background);
|
||||
|
||||
.chat-full-page-header {
|
||||
border-bottom: 1px solid var(--primary-low);
|
||||
background: var(--secondary);
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&__back-btn {
|
||||
width: 40px;
|
||||
min-width: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.chat-channel-title {
|
||||
.category-chat-name,
|
||||
.chat-name,
|
||||
.dm-usernames {
|
||||
color: var(--primary);
|
||||
display: inline;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.-not-following {
|
||||
.chat-channel-title {
|
||||
max-width: calc(100% - 50px);
|
||||
}
|
||||
.join-channel-btn {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
.c-navbar-container {
|
||||
position: sticky;
|
||||
top: var(--header-offset);
|
||||
}
|
||||
|
||||
.chat-messages-scroll {
|
||||
|
@ -240,67 +209,6 @@ body.has-full-page-chat {
|
|||
}
|
||||
}
|
||||
|
||||
.chat-full-page-header__left-actions {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.chat-full-page-header__title {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.chat-full-page-header__right-actions {
|
||||
align-items: stretch;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
gap: 0.5rem;
|
||||
font-size: var(--font-up-1);
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.chat-full-page-header {
|
||||
box-sizing: border-box;
|
||||
|
||||
.chat-channel-header-details {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex: 1;
|
||||
max-width: 100%;
|
||||
|
||||
.chat-channel-archive-status {
|
||||
text-align: right;
|
||||
padding-right: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-channel-title {
|
||||
margin: 0;
|
||||
max-width: 100%;
|
||||
|
||||
.d-icon:not(.d-icon-lock) {
|
||||
height: 1.25em;
|
||||
width: 1.25em;
|
||||
}
|
||||
|
||||
.category-chat-name,
|
||||
.dm-username {
|
||||
font-weight: 700;
|
||||
font-size: var(--font-up-1);
|
||||
line-height: var(--font-up-1);
|
||||
}
|
||||
|
||||
.dm-usernames {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
.chat-channel-retry-archive {
|
||||
display: flex;
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.user-preferences .chat-setting .controls {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
|
|
@ -7,10 +7,6 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
.new-channel-btn {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
.chat-channel-info {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
padding: 1rem;
|
||||
flex-direction: column;
|
||||
|
||||
|
|
|
@ -1,10 +1,3 @@
|
|||
//appears in: header of chat pane, channel info, preview card
|
||||
.chat-channel-title-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chat-channel-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
@ -7,6 +7,7 @@ body.composer-open .chat-drawer-outlet-container {
|
|||
top: -5px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
z-index: z("composer", "content");
|
||||
}
|
||||
|
||||
html:not(.rtl) {
|
||||
|
@ -30,36 +31,25 @@ html.rtl {
|
|||
right: var(--composer-right, 20px);
|
||||
left: 0;
|
||||
max-height: calc(100% - var(--header-offset) - 15px);
|
||||
|
||||
.rtl & {
|
||||
left: var(--composer-right, 20px);
|
||||
right: 0;
|
||||
}
|
||||
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
pointer-events: none !important;
|
||||
bottom: 0;
|
||||
|
||||
> * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.no-channel-title {
|
||||
font-weight: bold;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
&.composer-draft-collapsed {
|
||||
bottom: 40px;
|
||||
}
|
||||
|
||||
box-sizing: border-box;
|
||||
padding-bottom: var(--composer-height, 0);
|
||||
transition: all 100ms ease-in;
|
||||
transition-property: bottom, padding-bottom;
|
||||
|
||||
.rtl & {
|
||||
left: var(--composer-right, 20px);
|
||||
right: 0;
|
||||
}
|
||||
|
||||
> * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-drawer {
|
||||
|
@ -101,170 +91,11 @@ html.rtl {
|
|||
}
|
||||
}
|
||||
|
||||
.chat-drawer-header__left-actions {
|
||||
display: flex;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.chat-drawer-header__right-actions {
|
||||
display: flex;
|
||||
height: 2rem;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.chat-drawer-header__top-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chat-drawer-header__bottom-line {
|
||||
height: 1.5rem;
|
||||
display: flex;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.chat-drawer-header__title {
|
||||
@include ellipsis;
|
||||
display: flex;
|
||||
width: auto;
|
||||
font-weight: 700;
|
||||
padding: 0 0.5rem 0 0;
|
||||
cursor: pointer;
|
||||
height: 2rem;
|
||||
align-items: center;
|
||||
|
||||
.chat-drawer-header__top-line {
|
||||
padding: 0.25rem;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
a.chat-drawer-header__title {
|
||||
&:hover {
|
||||
.chat-drawer-header__top-line {
|
||||
background: var(--primary-low);
|
||||
border-radius: var(--d-border-radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-drawer-header__icon {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.chat-drawer-header__divider {
|
||||
margin: 0 0.25rem;
|
||||
}
|
||||
|
||||
.chat-drawer-header {
|
||||
box-sizing: border-box;
|
||||
border-bottom: solid 1px var(--primary-low);
|
||||
border-radius: var(--d-border-radius-large) var(--d-border-radius-large) 0 0;
|
||||
background: var(--primary-very-low);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
cursor: pointer;
|
||||
padding: 0.25rem;
|
||||
|
||||
.btn {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.chat-channel-title {
|
||||
font-weight: 700;
|
||||
width: 100%;
|
||||
|
||||
&__user-info {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chat-name,
|
||||
.chat-drawer-name,
|
||||
.category-chat-name,
|
||||
.dm-usernames {
|
||||
color: var(--primary);
|
||||
}
|
||||
.category-chat-badge,
|
||||
.chat-drawer-badge {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
.d-icon:not(.d-icon-lock) {
|
||||
width: 1.25em;
|
||||
height: 1.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.dm-usernames {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.d-icon:not(.d-icon-d-chat) {
|
||||
color: var(--primary-high);
|
||||
}
|
||||
.category-hashtag {
|
||||
padding: 2px 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&__close-btn,
|
||||
&__back-btn,
|
||||
&__full-screen-btn,
|
||||
&__thread-list-btn,
|
||||
&__expand-btn {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 100%;
|
||||
|
||||
&:hover:active {
|
||||
background: var(--primary-low);
|
||||
}
|
||||
|
||||
.d-icon {
|
||||
color: var(--primary-low-mid);
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&:visited {
|
||||
.d-icon {
|
||||
color: var(--primary-low-mid);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
background: none;
|
||||
|
||||
.d-icon {
|
||||
background: none;
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.d-icon {
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__thread-list-btn.has-unreads {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-drawer-content {
|
||||
@include chat-scrollbar();
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
min-height: 1px;
|
||||
padding-bottom: 0.25em;
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
@mixin chat-height($inset: 0px) {
|
||||
// desktop and mobile
|
||||
// 46px is the height of the navbar
|
||||
height: calc(
|
||||
var(--chat-vh, 1vh) * 100 - var(--header-offset, 0px) -
|
||||
var(--composer-height, 0px)
|
||||
var(--composer-height, 0px) - var(--chat-header-offset)
|
||||
);
|
||||
|
||||
// mobile with keyboard opened
|
||||
|
|
|
@ -26,5 +26,5 @@
|
|||
}
|
||||
|
||||
.full-page-chat .chat-mention-warnings {
|
||||
top: 4rem;
|
||||
top: 2rem;
|
||||
}
|
||||
|
|
|
@ -1,23 +1,58 @@
|
|||
.chat-navbar {
|
||||
flex-shrink: 0;
|
||||
.c-navbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 0.25rem;
|
||||
|
||||
&-container {
|
||||
padding-inline: 1rem;
|
||||
position: sticky;
|
||||
padding-inline: 0.5rem;
|
||||
border-bottom: 1px solid var(--primary-low);
|
||||
background: var(--secondary);
|
||||
top: var(--header-offset);
|
||||
height: 50px;
|
||||
height: var(--chat-header-offset);
|
||||
min-height: var(--chat-header-offset);
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
z-index: z("header") - 1;
|
||||
z-index: z("composer", "content") - 1;
|
||||
|
||||
&.-clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.single-select-header {
|
||||
padding: 0.3675rem 0.584rem;
|
||||
}
|
||||
|
||||
.c-navbar__channel-title {
|
||||
@include ellipsis();
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.c-navbar__title {
|
||||
@include ellipsis();
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.c-navbar__sub-title {
|
||||
line-height: var(--line-height-small);
|
||||
font-size: var(--font-down-1-rem);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.c-navbar__threads-list-button {
|
||||
gap: 0.25rem;
|
||||
|
||||
&.has-unreads {
|
||||
.d-icon-discourse-threads {
|
||||
color: var(--tertiary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-navbar__right-actions {
|
||||
.c-navbar__actions {
|
||||
list-style: none;
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
|
|
@ -18,32 +18,25 @@
|
|||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: var(--tertiary-low);
|
||||
padding: 0.5em 0 0.5em 1em;
|
||||
padding: 0.5rem;
|
||||
color: var(--primary);
|
||||
padding: 0.5em 0 0.5em 1em;
|
||||
min-width: 280px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.dismiss-btn {
|
||||
margin: 0 0.25em;
|
||||
color: var(--primary-medium);
|
||||
align-self: flex-start;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: transparent;
|
||||
.d-icon {
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
.d-icon {
|
||||
color: var(--primary-medium);
|
||||
background: var(--tertiary-medium);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.full-page-chat .chat-notices {
|
||||
top: 4rem;
|
||||
top: 2rem;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
.chat-side-panel-resizer {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
position: absolute;
|
||||
z-index: z("composer", "content") - 1;
|
||||
transition: background-color 0.15s 0.15s;
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
@mixin chat-channel-header-button {
|
||||
color: var(--primary-low-mid);
|
||||
padding: 0.25em 0.4em;
|
||||
|
||||
.d-icon {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
&:visited {
|
||||
color: var(--primary-low-mid);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--primary-medium);
|
||||
background: var(--primary-very-low);
|
||||
border-radius: var(--d-border-radius);
|
||||
|
||||
&:hover {
|
||||
.d-icon {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> * {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-channel {
|
||||
.chat-threads-list-button {
|
||||
@include chat-channel-header-button;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&.has-unreads {
|
||||
color: var(--tertiary-med-or-tertiary);
|
||||
gap: 0.25rem;
|
||||
|
||||
&:hover {
|
||||
color: var(--tertiary-hover);
|
||||
}
|
||||
}
|
||||
|
||||
.d-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.discourse-touch & {
|
||||
background: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
.discourse-touch & {
|
||||
background: var(--secondary-very-high) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.open-drawer-btn {
|
||||
@include chat-channel-header-button;
|
||||
}
|
||||
}
|
|
@ -1,32 +1,8 @@
|
|||
.chat-thread-header {
|
||||
height: var(--chat-header-offset);
|
||||
min-height: var(--chat-header-offset);
|
||||
border-bottom: 1px solid var(--primary-low);
|
||||
border-top: 1px solid var(--primary-low);
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-inline: 0.5rem;
|
||||
|
||||
.touch & {
|
||||
&__label {
|
||||
font-size: var(--font-up-1-rem);
|
||||
}
|
||||
}
|
||||
|
||||
.chat-thread__back-to-previous-route {
|
||||
padding: 0.5rem 0;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
&__left-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
.chat-thread-list-header {
|
||||
height: var(--chat-header-offset);
|
||||
min-height: var(--chat-header-offset);
|
||||
border-bottom: 1px solid var(--primary-low);
|
||||
border-top: 1px solid var(--primary-low);
|
||||
box-sizing: border-box;
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
.full-page-chat-header {
|
||||
display: flex;
|
||||
padding: 0.25rem;
|
||||
border-bottom: 1px solid var(--primary-low);
|
||||
justify-content: space-between;
|
||||
@include ellipsis;
|
||||
flex-direction: column;
|
||||
|
||||
.chat-channel-info-link {
|
||||
justify-self: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.full-page-chat-header__about-link {
|
||||
@include ellipsis;
|
||||
padding-right: 0.25rem;
|
||||
|
||||
.chat-channel-title__name {
|
||||
font-weight: 700;
|
||||
}
|
||||
.chat-channel-title {
|
||||
padding: 0.5rem 0.5rem 0.25rem 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.full-page-chat-header__members-link {
|
||||
padding: 0 0.5rem 0.5rem 0.5rem;
|
||||
font-size: var(--font-down-1);
|
||||
color: var(--primary-medium);
|
||||
|
||||
&:visited {
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
}
|
||||
|
||||
.full-page-chat-header__first-row {
|
||||
display: flex;
|
||||
height: 45px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.full-page-chat-header__second-row {
|
||||
display: flex;
|
||||
height: 32px;
|
||||
align-items: center;
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
@import "chat-unread-indicator";
|
||||
@import "chat-height-mixin";
|
||||
@import "chat-thread-header-buttons";
|
||||
@import "base-common";
|
||||
@import "sidebar-extensions";
|
||||
@import "chat-browse";
|
||||
|
@ -41,7 +40,6 @@
|
|||
@import "chat-transcript";
|
||||
@import "core-extensions";
|
||||
@import "dc-filter-input";
|
||||
@import "full-page-chat-header";
|
||||
@import "incoming-chat-webhooks";
|
||||
@import "reviewable-chat-message";
|
||||
@import "chat-thread-list-item";
|
||||
|
|
|
@ -12,13 +12,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.chat-full-page-header {
|
||||
padding: 0 1rem;
|
||||
height: var(--chat-header-offset);
|
||||
min-height: var(--chat-header-offset);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.chat-channel {
|
||||
.chat-messages-container {
|
||||
&.has-reply {
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
.chat-channel-title-wrapper {
|
||||
padding: 0.25rem;
|
||||
|
||||
&:hover {
|
||||
background: var(--primary-very-low);
|
||||
border-radius: var(--d-border-radius);
|
||||
}
|
||||
|
||||
.chat-channel-title {
|
||||
&__user-info {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
@import "base-desktop";
|
||||
@import "chat-channel-title";
|
||||
@import "chat-composer-uploads";
|
||||
@import "chat-index-drawer";
|
||||
@import "chat-index-full-page";
|
||||
|
|
|
@ -25,7 +25,7 @@ html.has-full-page-chat {
|
|||
grid-template-columns: 1fr;
|
||||
grid-template-areas: "threads";
|
||||
|
||||
.chat-channel {
|
||||
.c-routes-channel {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
@ -54,13 +54,6 @@ html.has-full-page-chat {
|
|||
.chat-drawer {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chat-full-page-header {
|
||||
background-color: var(--secondary);
|
||||
padding: 0 10px;
|
||||
height: 50px;
|
||||
min-height: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-container .channels-list .chat-channel-divider {
|
||||
|
@ -77,18 +70,6 @@ html.has-full-page-chat {
|
|||
}
|
||||
}
|
||||
|
||||
.chat-full-page-header {
|
||||
.chat-channel-header-details {
|
||||
.chat-channel-retry-archive {
|
||||
flex-direction: column;
|
||||
|
||||
.chat-channel-archive-failed-retry {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-message-separator {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ RSpec.describe "Browse page", type: :system do
|
|||
context "when on mobile", mobile: true do
|
||||
it "has a back button" do
|
||||
chat_page.visit_browse
|
||||
find(".chat-full-page-header__back-btn").click
|
||||
find(".c-navbar__back-button").click
|
||||
|
||||
expect(browse_page).to have_current_path("/chat")
|
||||
end
|
||||
|
@ -80,6 +80,7 @@ RSpec.describe "Browse page", type: :system do
|
|||
context "when results are found" do
|
||||
it "lists expected results" do
|
||||
chat_page.visit_browse
|
||||
|
||||
browse_page.search(category_channel_1.name)
|
||||
|
||||
expect(browse_page).to have_channel(name: category_channel_1.name)
|
||||
|
|
|
@ -18,7 +18,7 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
|
|||
it "redirects to browse page" do
|
||||
chat_page.visit_browse
|
||||
find(".chat-channel-card__setting").click
|
||||
find(".chat-full-page-header__back-btn").click
|
||||
find(".c-navbar__back-button").click
|
||||
|
||||
expect(page).to have_current_path("/chat/browse/open")
|
||||
end
|
||||
|
@ -29,8 +29,8 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
|
|||
context "when clicking back button" do
|
||||
it "redirects to channel page" do
|
||||
chat_page.visit_channel(channel_1)
|
||||
find(".chat-channel-title-wrapper").click
|
||||
find(".chat-full-page-header__back-btn").click
|
||||
find(".c-navbar__channel-title").click
|
||||
find(".c-navbar__back-button").click
|
||||
|
||||
expect(page).to have_current_path(chat.channel_path(channel_1.slug, channel_1.id))
|
||||
end
|
||||
|
|
|
@ -22,7 +22,7 @@ RSpec.describe "Drawer", type: :system do
|
|||
visit("/")
|
||||
chat_page.open_from_header
|
||||
drawer_page.open_channel(channel)
|
||||
page.find(".chat-channel-title").click
|
||||
page.find(".c-navbar__channel-title").click
|
||||
|
||||
expect(page).to have_current_path("/chat/c/#{channel.slug}/#{channel.id}/info/members")
|
||||
end
|
||||
|
@ -94,7 +94,7 @@ RSpec.describe "Drawer", type: :system do
|
|||
chat_page.open_from_header
|
||||
expect(page).to have_selector(".chat-drawer.is-expanded")
|
||||
|
||||
page.find(".chat-drawer-header").click
|
||||
page.find(".c-navbar").click
|
||||
|
||||
expect(page).to have_selector(".chat-drawer:not(.is-expanded)")
|
||||
end
|
||||
|
|
|
@ -337,7 +337,7 @@ RSpec.describe "Navigation", type: :system do
|
|||
context "when going back to channel from channel settings in full page" do
|
||||
it "activates the channel in the sidebar" do
|
||||
visit("/chat/c/#{category_channel.slug}/#{category_channel.id}/info/settings")
|
||||
find(".chat-full-page-header__back-btn").click
|
||||
find(".c-navbar__back-button").click
|
||||
expect(page).to have_content(message.message)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -93,10 +93,10 @@ module PageObjects
|
|||
end
|
||||
|
||||
def minimize_full_page
|
||||
find(".open-drawer-btn").click
|
||||
find(".c-navbar__open-drawer-button").click
|
||||
end
|
||||
|
||||
NEW_CHANNEL_BUTTON_SELECTOR = ".new-channel-btn"
|
||||
NEW_CHANNEL_BUTTON_SELECTOR = ".c-navbar__new-channel-button"
|
||||
|
||||
def new_channel_button
|
||||
find(NEW_CHANNEL_BUTTON_SELECTOR)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user