UX: implements swipe on row channel (#23436)
On mobile swiping a channel row will now show a "Remove" option. Holding this to the end will now remove this row from your list of followed direct message channels. Co-authored-by: chapoi <101828855+chapoi@users.noreply.github.com>
This commit is contained in:
parent
87d0336f05
commit
b8d5f951f6
|
@ -43,7 +43,14 @@
|
|||
</LinkTo>
|
||||
</div>
|
||||
|
||||
<div id="public-channels" class={{this.publicChannelClasses}}>
|
||||
<div
|
||||
id="public-channels"
|
||||
class={{concat-class
|
||||
"channels-list-container"
|
||||
"public-channels"
|
||||
(if this.inSidebar "collapsible-sidebar-section")
|
||||
}}
|
||||
>
|
||||
{{#if this.publicChannelsEmpty}}
|
||||
<div class="public-channel-empty-message">
|
||||
<span class="channel-title">{{i18n "chat.no_public_channels"}}</span>
|
||||
|
|
|
@ -65,12 +65,6 @@ export default class ChannelsList extends Component {
|
|||
return this.chat.userCanDirectMessage;
|
||||
}
|
||||
|
||||
get publicChannelClasses() {
|
||||
return `channels-list-container public-channels ${
|
||||
this.inSidebar ? "collapsible-sidebar-section" : ""
|
||||
}`;
|
||||
}
|
||||
|
||||
get displayPublicChannels() {
|
||||
if (!this.siteSettings.enable_public_channels) {
|
||||
return false;
|
||||
|
|
|
@ -0,0 +1,252 @@
|
|||
import { inject as service } from "@ember/service";
|
||||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import eq from "truth-helpers/helpers/eq";
|
||||
import and from "truth-helpers/helpers/and";
|
||||
import ChatChannelTitle from "discourse/plugins/chat/discourse/components/chat-channel-title";
|
||||
import ChatChannelMetadata from "discourse/plugins/chat/discourse/components/chat-channel-metadata";
|
||||
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||
import ToggleChannelMembershipButton from "discourse/plugins/chat/discourse/components/toggle-channel-membership-button";
|
||||
import willDestroy from "@ember/render-modifiers/modifiers/will-destroy";
|
||||
import { hash } from "@ember/helper";
|
||||
import I18n from "I18n";
|
||||
import { modifier } from "ember-modifier";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import discourseLater from "discourse-common/lib/later";
|
||||
import { cancel } from "@ember/runloop";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
const RESET_CLASS = "-reset";
|
||||
const FADEOUT_CLASS = "-fade-out";
|
||||
|
||||
export default class ChatChannelRow extends Component {
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<LinkTo
|
||||
@route="chat.channel"
|
||||
@models={{@channel.routeModels}}
|
||||
class={{concatClass
|
||||
"chat-channel-row"
|
||||
(if @channel.focused "focused")
|
||||
(if @channel.currentUserMembership.muted "muted")
|
||||
(if @options.leaveButton "can-leave")
|
||||
(if (eq this.chat.activeChannel.id @channel.id) "active")
|
||||
(if this.channelHasUnread "has-unread")
|
||||
}}
|
||||
tabindex="0"
|
||||
data-chat-channel-id={{@channel.id}}
|
||||
{{didInsert this.startTrackingStatus}}
|
||||
{{willDestroy this.stopTrackingStatus}}
|
||||
{{(if this.shouldHandleSwipe (modifier this.registerSwipableRow))}}
|
||||
{{(if this.shouldHandleSwipe (modifier this.handleSwipe))}}
|
||||
{{(if this.shouldRemoveChannel (modifier this.onRemoveChannel))}}
|
||||
{{(if this.shouldResetRow (modifier this.onResetRow))}}
|
||||
>
|
||||
<ChatChannelTitle @channel={{@channel}} />
|
||||
<ChatChannelMetadata @channel={{@channel}} @unreadIndicator={{true}} />
|
||||
|
||||
{{#if
|
||||
(and @options.leaveButton @channel.isFollowing this.site.desktopView)
|
||||
}}
|
||||
<ToggleChannelMembershipButton
|
||||
@channel={{@channel}}
|
||||
@options={{hash
|
||||
leaveClass="btn-flat chat-channel-leave-btn"
|
||||
labelType="none"
|
||||
leaveIcon="times"
|
||||
leaveTitle=(if
|
||||
@channel.isDirectMessageChannel
|
||||
this.leaveDirectMessageLabel
|
||||
this.leaveChannelLabel
|
||||
)
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.shouldHandleSwipe}}
|
||||
<div
|
||||
class={{concatClass
|
||||
"chat-channel-row__action-btn"
|
||||
(if this.isCancelling "-cancel" "-leave")
|
||||
}}
|
||||
{{this.registerActionButton}}
|
||||
{{this.positionActionButton}}
|
||||
>
|
||||
{{#if this.isCancelling}}
|
||||
{{this.cancelActionLabel}}
|
||||
{{else}}
|
||||
{{this.removeActionLabel}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
</template>
|
||||
|
||||
@service router;
|
||||
@service chat;
|
||||
@service capabilities;
|
||||
@service currentUser;
|
||||
@service site;
|
||||
@service api;
|
||||
|
||||
@tracked shouldRemoveChannel = false;
|
||||
@tracked hasReachedThreshold = false;
|
||||
@tracked isCancelling = false;
|
||||
@tracked shouldResetRow = false;
|
||||
@tracked actionButton;
|
||||
@tracked swipableRow;
|
||||
|
||||
positionActionButton = modifier((element) => {
|
||||
element.style.left = "100%";
|
||||
});
|
||||
|
||||
registerActionButton = modifier((element) => {
|
||||
this.actionButton = element;
|
||||
});
|
||||
|
||||
registerSwipableRow = modifier((element) => {
|
||||
this.swipableRow = element;
|
||||
});
|
||||
|
||||
onRemoveChannel = modifier(() => {
|
||||
this.swipableRow.classList.add(FADEOUT_CLASS);
|
||||
|
||||
const handler = discourseLater(() => {
|
||||
this.chat.unfollowChannel(this.args.channel).catch(popupAjaxError);
|
||||
}, 250);
|
||||
|
||||
return () => {
|
||||
cancel(handler);
|
||||
};
|
||||
});
|
||||
|
||||
handleSwipe = modifier((element) => {
|
||||
element.addEventListener("touchstart", this.onSwipeStart, {
|
||||
passive: true,
|
||||
});
|
||||
element.addEventListener("touchmove", this.onSwipe);
|
||||
element.addEventListener("touchend", this.onSwipeEnd);
|
||||
|
||||
return () => {
|
||||
element.removeEventListener("touchstart", this.onSwipeStart);
|
||||
element.removeEventListener("touchmove", this.onSwipe);
|
||||
element.removeEventListener("touchend", this.onSwipeEnd);
|
||||
};
|
||||
});
|
||||
|
||||
onResetRow = modifier(() => {
|
||||
this.swipableRow.classList.add(RESET_CLASS);
|
||||
this.swipableRow.style.left = "0px";
|
||||
|
||||
const handler = discourseLater(() => {
|
||||
this.isCancelling = false;
|
||||
this.hasReachedThreshold = false;
|
||||
this.shouldResetRow = false;
|
||||
this.swipableRow.classList.remove(RESET_CLASS);
|
||||
}, 250);
|
||||
|
||||
return () => {
|
||||
cancel(handler);
|
||||
this.swipableRow.classList.remove(RESET_CLASS);
|
||||
};
|
||||
});
|
||||
|
||||
_lastX = null;
|
||||
_towardsThreshold = false;
|
||||
|
||||
@bind
|
||||
onSwipeStart(event) {
|
||||
this.hasReachedThreshold = false;
|
||||
this.isCancelling = false;
|
||||
this._lastX = this.initialX = event.changedTouches[0].screenX;
|
||||
}
|
||||
|
||||
@bind
|
||||
onSwipe(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const touchX = event.changedTouches[0].screenX;
|
||||
const diff = this.initialX - touchX;
|
||||
|
||||
// we don't state to be too sensitive to the touch
|
||||
if (Math.abs(this._lastX - touchX) > 5) {
|
||||
this._towardsThreshold = this._lastX >= touchX;
|
||||
this._lastX = touchX;
|
||||
}
|
||||
|
||||
// ensures we will go back to the initial position when swiping very fast
|
||||
if (diff < 10) {
|
||||
this.isCancelling = false;
|
||||
this.hasReachedThreshold = false;
|
||||
this.swipableRow.style.left = "0px";
|
||||
return;
|
||||
}
|
||||
|
||||
if (diff >= window.innerWidth / 3) {
|
||||
this.isCancelling = false;
|
||||
this.hasReachedThreshold = true;
|
||||
return;
|
||||
} else {
|
||||
this.isCancelling = !this._towardsThreshold;
|
||||
}
|
||||
|
||||
this.actionButton.style.width = diff + "px";
|
||||
this.swipableRow.style.left = -(this.initialX - touchX) + "px";
|
||||
}
|
||||
|
||||
@bind
|
||||
onSwipeEnd(event) {
|
||||
this._lastX = null;
|
||||
const diff = this.initialX - event.changedTouches[0].screenX;
|
||||
|
||||
if (diff >= window.innerWidth / 3) {
|
||||
this.swipableRow.style.left = "0px";
|
||||
this.shouldRemoveChannel = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.isCancelling = true;
|
||||
this.shouldResetRow = true;
|
||||
}
|
||||
|
||||
get shouldHandleSwipe() {
|
||||
return this.capabilities.touch && this.args.channel.isDirectMessageChannel;
|
||||
}
|
||||
|
||||
get cancelActionLabel() {
|
||||
return I18n.t("cancel_value");
|
||||
}
|
||||
|
||||
get removeActionLabel() {
|
||||
return I18n.t("chat.remove");
|
||||
}
|
||||
|
||||
get leaveDirectMessageLabel() {
|
||||
return I18n.t("chat.direct_messages.leave");
|
||||
}
|
||||
|
||||
get leaveChannelLabel() {
|
||||
return I18n.t("chat.channel_settings.leave_channel");
|
||||
}
|
||||
|
||||
get channelHasUnread() {
|
||||
return this.args.channel.tracking.unreadCount > 0;
|
||||
}
|
||||
|
||||
get #firstDirectMessageUser() {
|
||||
return this.args.channel?.chatable?.users?.firstObject;
|
||||
}
|
||||
|
||||
@action
|
||||
startTrackingStatus() {
|
||||
this.#firstDirectMessageUser?.trackStatus();
|
||||
}
|
||||
|
||||
@action
|
||||
stopTrackingStatus() {
|
||||
this.#firstDirectMessageUser?.stopTrackingStatus();
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
<LinkTo
|
||||
@route="chat.channel"
|
||||
@models={{@channel.routeModels}}
|
||||
class={{concat-class
|
||||
"chat-channel-row"
|
||||
(if @channel.focused "focused")
|
||||
(if @channel.currentUserMembership.muted "muted")
|
||||
(if @options.leaveButton "can-leave")
|
||||
(if (eq this.chat.activeChannel.id @channel.id) "active")
|
||||
(if this.channelHasUnread "has-unread")
|
||||
}}
|
||||
tabindex="0"
|
||||
data-chat-channel-id={{@channel.id}}
|
||||
{{did-insert this.startTrackingStatus}}
|
||||
{{will-destroy this.stopTrackingStatus}}
|
||||
>
|
||||
<ChatChannelTitle @channel={{@channel}} />
|
||||
<ChatChannelMetadata @channel={{@channel}} @unreadIndicator={{true}} />
|
||||
|
||||
{{#if (and @options.leaveButton @channel.isFollowing this.site.desktopView)}}
|
||||
<ToggleChannelMembershipButton
|
||||
@channel={{@channel}}
|
||||
@options={{hash
|
||||
leaveClass="btn-flat chat-channel-leave-btn"
|
||||
labelType="none"
|
||||
leaveIcon="times"
|
||||
leaveTitle=(if
|
||||
@channel.isDirectMessageChannel
|
||||
(i18n "chat.direct_messages.leave")
|
||||
(i18n "chat.channel_settings.leave_channel")
|
||||
)
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
</LinkTo>
|
|
@ -1,28 +0,0 @@
|
|||
import { inject as service } from "@ember/service";
|
||||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
|
||||
export default class ChatChannelRow extends Component {
|
||||
@service router;
|
||||
@service chat;
|
||||
@service currentUser;
|
||||
@service site;
|
||||
|
||||
@action
|
||||
startTrackingStatus() {
|
||||
this.#firstDirectMessageUser?.trackStatus();
|
||||
}
|
||||
|
||||
@action
|
||||
stopTrackingStatus() {
|
||||
this.#firstDirectMessageUser?.stopTrackingStatus();
|
||||
}
|
||||
|
||||
get channelHasUnread() {
|
||||
return this.args.channel.tracking.unreadCount > 0;
|
||||
}
|
||||
|
||||
get #firstDirectMessageUser() {
|
||||
return this.args.channel?.chatable?.users?.firstObject;
|
||||
}
|
||||
}
|
|
@ -12,6 +12,26 @@ import discourseDebounce from "discourse-common/lib/debounce";
|
|||
import { bind } from "discourse-common/utils/decorators";
|
||||
import { updateUserStatusOnMention } from "discourse/lib/update-user-status-on-mention";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import ChatMessageSeparatorDate from "discourse/plugins/chat/discourse/components/chat-message-separator-date";
|
||||
import ChatMessageSeparatorNew from "discourse/plugins/chat/discourse/components/chat-message-separator-new";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import ChatMessageInReplyToIndicator from "discourse/plugins/chat/discourse/components/chat-message-in-reply-to-indicator";
|
||||
import ChatMessageLeftGutter from "discourse/plugins/chat/discourse/components/chat/message/left-gutter";
|
||||
import ChatMessageAvatar from "discourse/plugins/chat/discourse/components/chat/message/avatar";
|
||||
import ChatMessageError from "discourse/plugins/chat/discourse/components/chat/message/error";
|
||||
import ChatMessageInfo from "discourse/plugins/chat/discourse/components/chat/message/info";
|
||||
import ChatMessageText from "discourse/plugins/chat/discourse/components/chat-message-text";
|
||||
import ChatMessageReaction from "discourse/plugins/chat/discourse/components/chat-message-reaction";
|
||||
import ChatMessageThreadIndicator from "discourse/plugins/chat/discourse/components/chat-message-thread-indicator";
|
||||
import eq from "truth-helpers/helpers/eq";
|
||||
import not from "truth-helpers/helpers/not";
|
||||
import { on } from "@ember/modifier";
|
||||
import { Input } from "@ember/component";
|
||||
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||
import didUpdate from "@ember/render-modifiers/modifiers/did-update";
|
||||
import willDestroy from "@ember/render-modifiers/modifiers/will-destroy";
|
||||
import ChatOnLongPress from "discourse/plugins/chat/discourse/modifiers/chat/on-long-press";
|
||||
|
||||
let _chatMessageDecorators = [];
|
||||
let _tippyInstances = [];
|
||||
|
@ -28,6 +48,139 @@ export const MENTION_KEYWORDS = ["here", "all"];
|
|||
export const MESSAGE_CONTEXT_THREAD = "thread";
|
||||
|
||||
export default class ChatMessage extends Component {
|
||||
<template>
|
||||
{{! template-lint-disable no-invalid-interactive }}
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
{{#if this.shouldRender}}
|
||||
{{#if (eq @context "channel")}}
|
||||
<ChatMessageSeparatorDate
|
||||
@fetchMessagesByDate={{@fetchMessagesByDate}}
|
||||
@message={{@message}}
|
||||
/>
|
||||
<ChatMessageSeparatorNew @message={{@message}} />
|
||||
{{/if}}
|
||||
|
||||
<div
|
||||
class={{concatClass
|
||||
"chat-message-container"
|
||||
(if this.pane.selectingMessages "-selectable")
|
||||
(if @message.highlighted "-highlighted")
|
||||
(if (eq @message.user.id this.currentUser.id) "is-by-current-user")
|
||||
(if @message.staged "-staged" "-persisted")
|
||||
(if this.hasActiveState "-active")
|
||||
(if @message.bookmark "-bookmarked")
|
||||
(if @message.deletedAt "-deleted")
|
||||
(if @message.selected "-selected")
|
||||
(if @message.error "-errored")
|
||||
(if this.showThreadIndicator "has-thread-indicator")
|
||||
(if this.hideUserInfo "-user-info-hidden")
|
||||
(if this.hasReply "has-reply")
|
||||
}}
|
||||
data-id={{@message.id}}
|
||||
data-thread-id={{@message.thread.id}}
|
||||
{{didInsert this.didInsertMessage}}
|
||||
{{didUpdate this.didUpdateMessageId @message.id}}
|
||||
{{didUpdate this.didUpdateMessageVersion @message.version}}
|
||||
{{willDestroy this.willDestroyMessage}}
|
||||
{{on "mouseenter" this.onMouseEnter passive=true}}
|
||||
{{on "mouseleave" this.onMouseLeave passive=true}}
|
||||
{{on "mousemove" this.onMouseMove passive=true}}
|
||||
{{ChatOnLongPress
|
||||
this.onLongPressStart
|
||||
this.onLongPressEnd
|
||||
this.onLongPressCancel
|
||||
}}
|
||||
...attributes
|
||||
>
|
||||
{{#if this.show}}
|
||||
{{#if this.pane.selectingMessages}}
|
||||
<Input
|
||||
@type="checkbox"
|
||||
class="chat-message-selector"
|
||||
@checked={{@message.selected}}
|
||||
{{on "click" this.toggleChecked}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.deletedAndCollapsed}}
|
||||
<div class="chat-message-text -deleted">
|
||||
<DButton
|
||||
@action={{this.expand}}
|
||||
@translatedLabel={{this.deletedMessageLabel}}
|
||||
class="btn-flat chat-message-expand"
|
||||
/>
|
||||
</div>
|
||||
{{else if this.hiddenAndCollapsed}}
|
||||
<div class="chat-message-text -hidden">
|
||||
<DButton
|
||||
@action={{this.expand}}
|
||||
@label="chat.hidden"
|
||||
class="btn-flat chat-message-expand"
|
||||
/>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="chat-message">
|
||||
{{#unless this.hideReplyToInfo}}
|
||||
<ChatMessageInReplyToIndicator @message={{@message}} />
|
||||
{{/unless}}
|
||||
|
||||
{{#if this.hideUserInfo}}
|
||||
<ChatMessageLeftGutter @message={{@message}} />
|
||||
{{else}}
|
||||
<ChatMessageAvatar @message={{@message}} />
|
||||
{{/if}}
|
||||
|
||||
<div class="chat-message-content">
|
||||
<ChatMessageInfo
|
||||
@message={{@message}}
|
||||
@show={{not this.hideUserInfo}}
|
||||
/>
|
||||
|
||||
<ChatMessageText
|
||||
@cooked={{@message.cooked}}
|
||||
@uploads={{@message.uploads}}
|
||||
@edited={{@message.edited}}
|
||||
>
|
||||
{{#if @message.reactions.length}}
|
||||
<div class="chat-message-reaction-list">
|
||||
{{#each @message.reactions as |reaction|}}
|
||||
<ChatMessageReaction
|
||||
@reaction={{reaction}}
|
||||
@onReaction={{this.messageInteractor.react}}
|
||||
@message={{@message}}
|
||||
@showTooltip={{true}}
|
||||
/>
|
||||
{{/each}}
|
||||
|
||||
{{#if this.shouldRenderOpenEmojiPickerButton}}
|
||||
<DButton
|
||||
@action={{this.messageInteractor.openEmojiPicker}}
|
||||
@icon="discourse-emojis"
|
||||
@title="chat.react"
|
||||
@forwardEvent={{true}}
|
||||
class="chat-message-react-btn"
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</ChatMessageText>
|
||||
|
||||
<ChatMessageError
|
||||
@message={{@message}}
|
||||
@onRetry={{@resendStagedMessage}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{{#if this.showThreadIndicator}}
|
||||
<ChatMessageThreadIndicator @message={{@message}} />
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
@service site;
|
||||
@service dialog;
|
||||
@service currentUser;
|
|
@ -1,130 +0,0 @@
|
|||
{{! template-lint-disable no-invalid-interactive }}
|
||||
|
||||
{{#if this.shouldRender}}
|
||||
{{#if (eq @context "channel")}}
|
||||
<ChatMessageSeparatorDate
|
||||
@fetchMessagesByDate={{@fetchMessagesByDate}}
|
||||
@message={{@message}}
|
||||
/>
|
||||
<ChatMessageSeparatorNew @message={{@message}} />
|
||||
{{/if}}
|
||||
|
||||
<div
|
||||
class={{concat-class
|
||||
"chat-message-container"
|
||||
(if this.pane.selectingMessages "-selectable")
|
||||
(if @message.highlighted "-highlighted")
|
||||
(if (eq @message.user.id this.currentUser.id) "is-by-current-user")
|
||||
(if @message.staged "-staged" "-persisted")
|
||||
(if this.hasActiveState "-active")
|
||||
(if @message.bookmark "-bookmarked")
|
||||
(if @message.deletedAt "-deleted")
|
||||
(if @message.selected "-selected")
|
||||
(if @message.error "-errored")
|
||||
(if this.showThreadIndicator "has-thread-indicator")
|
||||
(if this.hideUserInfo "-user-info-hidden")
|
||||
(if this.hasReply "has-reply")
|
||||
}}
|
||||
data-id={{@message.id}}
|
||||
data-thread-id={{@message.thread.id}}
|
||||
{{did-insert this.didInsertMessage}}
|
||||
{{did-update this.didUpdateMessageId @message.id}}
|
||||
{{did-update this.didUpdateMessageVersion @message.version}}
|
||||
{{will-destroy this.willDestroyMessage}}
|
||||
{{on "mouseenter" this.onMouseEnter passive=true}}
|
||||
{{on "mouseleave" this.onMouseLeave passive=true}}
|
||||
{{on "mousemove" this.onMouseMove passive=true}}
|
||||
{{chat/on-long-press
|
||||
this.onLongPressStart
|
||||
this.onLongPressEnd
|
||||
this.onLongPressCancel
|
||||
}}
|
||||
...attributes
|
||||
>
|
||||
{{#if this.show}}
|
||||
{{#if this.pane.selectingMessages}}
|
||||
<Input
|
||||
@type="checkbox"
|
||||
class="chat-message-selector"
|
||||
@checked={{@message.selected}}
|
||||
{{on "click" this.toggleChecked}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.deletedAndCollapsed}}
|
||||
<div class="chat-message-text -deleted">
|
||||
<DButton
|
||||
@action={{this.expand}}
|
||||
@translatedLabel={{this.deletedMessageLabel}}
|
||||
class="btn-flat chat-message-expand"
|
||||
/>
|
||||
</div>
|
||||
{{else if this.hiddenAndCollapsed}}
|
||||
<div class="chat-message-text -hidden">
|
||||
<DButton
|
||||
@action={{this.expand}}
|
||||
@label="chat.hidden"
|
||||
class="btn-flat chat-message-expand"
|
||||
/>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="chat-message">
|
||||
{{#unless this.hideReplyToInfo}}
|
||||
<ChatMessageInReplyToIndicator @message={{@message}} />
|
||||
{{/unless}}
|
||||
|
||||
{{#if this.hideUserInfo}}
|
||||
<Chat::Message::LeftGutter @message={{@message}} />
|
||||
{{else}}
|
||||
<Chat::Message::Avatar @message={{@message}} />
|
||||
{{/if}}
|
||||
|
||||
<div class="chat-message-content">
|
||||
<Chat::Message::Info
|
||||
@message={{@message}}
|
||||
@show={{not this.hideUserInfo}}
|
||||
/>
|
||||
|
||||
<ChatMessageText
|
||||
@cooked={{@message.cooked}}
|
||||
@uploads={{@message.uploads}}
|
||||
@edited={{@message.edited}}
|
||||
>
|
||||
{{#if @message.reactions.length}}
|
||||
<div class="chat-message-reaction-list">
|
||||
{{#each @message.reactions as |reaction|}}
|
||||
<ChatMessageReaction
|
||||
@reaction={{reaction}}
|
||||
@onReaction={{this.messageInteractor.react}}
|
||||
@message={{@message}}
|
||||
@showTooltip={{true}}
|
||||
/>
|
||||
{{/each}}
|
||||
|
||||
{{#if this.shouldRenderOpenEmojiPickerButton}}
|
||||
<DButton
|
||||
@action={{this.messageInteractor.openEmojiPicker}}
|
||||
@icon="discourse-emojis"
|
||||
@title="chat.react"
|
||||
@forwardEvent={{true}}
|
||||
class="chat-message-react-btn"
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</ChatMessageText>
|
||||
|
||||
<Chat::Message::Error
|
||||
@message={{@message}}
|
||||
@onRetry={{@resendStagedMessage}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{{#if this.showThreadIndicator}}
|
||||
<ChatMessageThreadIndicator @message={{@message}} />
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
|
@ -0,0 +1,146 @@
|
|||
.chat-channel-row {
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
color: var(--primary-high);
|
||||
|
||||
@media (hover: none) {
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--primary-low);
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover,
|
||||
&.active {
|
||||
background: var(--primary-very-low);
|
||||
}
|
||||
|
||||
&.can-leave:hover {
|
||||
.toggle-channel-membership-button.-leave {
|
||||
display: block;
|
||||
|
||||
> * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-channel-metadata {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
.chat-channel-title {
|
||||
&,
|
||||
.category-chat-name,
|
||||
.dm-usernames {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.d-icon-lock {
|
||||
background-color: var(--primary-low);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:visited {
|
||||
color: var(--primary-high);
|
||||
}
|
||||
|
||||
&.muted {
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
.chat-channel-title {
|
||||
&__users-count {
|
||||
width: var(--channel-list-avatar-size);
|
||||
height: var(--channel-list-avatar-size);
|
||||
padding: 0;
|
||||
font-size: var(--font-up-1);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
.chat-user-avatar {
|
||||
img {
|
||||
width: calc(var(--channel-list-avatar-size) - 2px);
|
||||
height: calc(var(--channel-list-avatar-size) - 2px);
|
||||
}
|
||||
}
|
||||
}
|
||||
&__user-info {
|
||||
@include ellipsis;
|
||||
}
|
||||
&__usernames {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
}
|
||||
.user-status-message {
|
||||
display: inline-block;
|
||||
font-size: var(--font-down-2);
|
||||
margin-right: 0.5rem;
|
||||
|
||||
&-description {
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-channel-metadata {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
flex-direction: column;
|
||||
margin-left: 0.5em;
|
||||
|
||||
&__date {
|
||||
color: var(--primary-high);
|
||||
font-size: var(--font-down-2);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.chat-channel-unread-indicator {
|
||||
@include chat-unread-indicator;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
|
||||
&.-urgent {
|
||||
width: auto;
|
||||
height: auto;
|
||||
min-width: 0.6em;
|
||||
padding: 0.3em 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.unfollowing {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.toggle-channel-membership-button.-leave {
|
||||
display: none;
|
||||
margin-left: auto;
|
||||
}
|
||||
.badge-wrapper {
|
||||
align-items: center;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.emoji {
|
||||
margin-left: 0.3em;
|
||||
}
|
||||
}
|
|
@ -88,151 +88,4 @@
|
|||
padding-top: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-channel-row {
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
color: var(--primary-high);
|
||||
|
||||
@media (hover: none) {
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--primary-low);
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover,
|
||||
&.active {
|
||||
background: var(--primary-very-low);
|
||||
}
|
||||
|
||||
&.can-leave:hover {
|
||||
.toggle-channel-membership-button.-leave {
|
||||
display: block;
|
||||
|
||||
> * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-channel-metadata {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
.chat-channel-title {
|
||||
&,
|
||||
.category-chat-name,
|
||||
.dm-usernames {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.d-icon-lock {
|
||||
background-color: var(--primary-low);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:visited {
|
||||
color: var(--primary-high);
|
||||
}
|
||||
|
||||
&.muted {
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
.chat-channel-title {
|
||||
&__users-count {
|
||||
width: var(--channel-list-avatar-size);
|
||||
height: var(--channel-list-avatar-size);
|
||||
padding: 0;
|
||||
font-size: var(--font-up-1);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
.chat-user-avatar {
|
||||
img {
|
||||
width: calc(var(--channel-list-avatar-size) - 2px);
|
||||
height: calc(var(--channel-list-avatar-size) - 2px);
|
||||
}
|
||||
}
|
||||
}
|
||||
&__user-info {
|
||||
@include ellipsis;
|
||||
}
|
||||
&__usernames {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
}
|
||||
.user-status-message {
|
||||
display: inline-block;
|
||||
font-size: var(--font-down-2);
|
||||
margin-right: 0.5rem;
|
||||
|
||||
&-description {
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-channel-metadata {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
flex-direction: column;
|
||||
margin-left: 0.5em;
|
||||
|
||||
&__date {
|
||||
color: var(--primary-high);
|
||||
font-size: var(--font-down-2);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.chat-channel-unread-indicator {
|
||||
@include chat-unread-indicator;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
|
||||
&.-urgent {
|
||||
width: auto;
|
||||
height: auto;
|
||||
min-width: 0.6em;
|
||||
padding: 0.3em 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.unfollowing {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.toggle-channel-membership-button.-leave {
|
||||
display: none;
|
||||
margin-left: auto;
|
||||
}
|
||||
.badge-wrapper {
|
||||
align-items: center;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.emoji {
|
||||
margin-left: 0.3em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,3 +63,4 @@
|
|||
@import "chat-modal-channel-summary";
|
||||
@import "chat-modal-move-message-to-channel";
|
||||
@import "chat-scroll-to-bottom";
|
||||
@import "chat-channel-row";
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
.chat-channel-row {
|
||||
height: 4em;
|
||||
margin: 0;
|
||||
padding: 0 1.5rem;
|
||||
border-radius: 0;
|
||||
border-bottom: 1px solid var(--primary-low);
|
||||
transition: height 0.25s ease-in-out, opacity 0.25s ease-out;
|
||||
transform-origin: top center;
|
||||
will-change: height, left;
|
||||
|
||||
&__action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
padding-inline: 1rem;
|
||||
|
||||
&.-cancel {
|
||||
background: var(--primary-very-low);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
&.-leave {
|
||||
background: var(--danger);
|
||||
color: var(--primary-very-low);
|
||||
}
|
||||
}
|
||||
|
||||
&__action-btn-icon {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
&.-fade-out {
|
||||
background-color: var(--danger-low);
|
||||
height: 0 !important;
|
||||
overflow: hidden;
|
||||
opacity: 0.5 !important;
|
||||
}
|
||||
|
||||
&.-reset {
|
||||
transition: left 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
.chat-channel-metadata {
|
||||
.chat-channel-unread-indicator {
|
||||
font-size: var(--font-down-2);
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
&__date {
|
||||
font-size: var(--font-down-2);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,24 +10,7 @@
|
|||
|
||||
.channels-list-container {
|
||||
background: var(--secondary);
|
||||
}
|
||||
|
||||
.chat-channel-row {
|
||||
height: 4em;
|
||||
margin: 0;
|
||||
padding: 0 1.5rem;
|
||||
border-radius: 0;
|
||||
border-bottom: 1px solid var(--primary-low);
|
||||
|
||||
.chat-channel-metadata {
|
||||
.chat-channel-unread-indicator {
|
||||
font-size: var(--font-down-2);
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
&__date {
|
||||
font-size: var(--font-down-2);
|
||||
}
|
||||
}
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chat-channel-divider {
|
||||
|
|
|
@ -14,3 +14,4 @@
|
|||
@import "chat-modal-thread-settings";
|
||||
@import "chat-message-thread-indicator";
|
||||
@import "chat-message-creator";
|
||||
@import "chat-channel-row";
|
||||
|
|
|
@ -83,6 +83,7 @@ en:
|
|||
|
||||
click_to_join: "Click here to view available channels."
|
||||
close: "Close"
|
||||
remove: "Remove"
|
||||
collapse: "Collapse Chat Drawer"
|
||||
expand: "Expand Chat Drawer"
|
||||
confirm_flag: "Are you sure you want to flag %{username}'s message?"
|
||||
|
|
|
@ -27,7 +27,10 @@ RSpec.describe "List channels | mobile", type: :system, mobile: true do
|
|||
context "when not member of the channel" do
|
||||
it "doesn’t show the channel" do
|
||||
visit("/chat")
|
||||
expect(page.find(".public-channels")).to have_no_content(category_channel_1.name)
|
||||
|
||||
expect(page.find(".public-channels", visible: :all)).to have_no_content(
|
||||
category_channel_1.name,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue