UX: chat drawer increase unread channel visibility (#28731)
This change increases the visibility of unread channels to make them stand out more in drawer mode (desktop). When a channel is unread: - it floats to the top; - when multiple channels are unread, they are sorted alphabetically (equal to how it’s done on mobile) - the unread indicator blue dot moves to directly right of the channel name
This commit is contained in:
parent
67ce50c141
commit
e991574389
|
@ -5,10 +5,15 @@ import { htmlSafe } from "@ember/template";
|
|||
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||
import UserStatusMessage from "discourse/components/user-status-message";
|
||||
import replaceEmoji from "discourse/helpers/replace-emoji";
|
||||
import ChatChannelUnreadIndicator from "../chat-channel-unread-indicator";
|
||||
|
||||
export default class ChatChannelName extends Component {
|
||||
@service currentUser;
|
||||
|
||||
get unreadIndicator() {
|
||||
return this.args.unreadIndicator ?? false;
|
||||
}
|
||||
|
||||
get firstUser() {
|
||||
return this.args.channel.chatable.users[0];
|
||||
}
|
||||
|
@ -37,27 +42,43 @@ export default class ChatChannelName extends Component {
|
|||
}
|
||||
|
||||
get showUserStatus() {
|
||||
if (!this.args.channel.isDirectMessageChannel) {
|
||||
return false;
|
||||
}
|
||||
return !!(this.users.length === 1 && this.users[0].status);
|
||||
}
|
||||
|
||||
get channelTitle() {
|
||||
if (this.args.channel.isDirectMessageChannel) {
|
||||
return this.groupDirectMessage
|
||||
? this.groupsDirectMessageTitle
|
||||
: this.firstUser.username;
|
||||
}
|
||||
return this.args.channel.title;
|
||||
}
|
||||
|
||||
get showPluginOutlet() {
|
||||
return this.args.channel.isDirectMessageChannel && !this.groupDirectMessage;
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="chat-channel-name">
|
||||
{{#if @channel.isDirectMessageChannel}}
|
||||
{{#if this.groupDirectMessage}}
|
||||
<span class="chat-channel-name__label">
|
||||
{{this.groupsDirectMessageTitle}}
|
||||
</span>
|
||||
{{else}}
|
||||
<span class="chat-channel-name__label">
|
||||
{{this.firstUser.username}}
|
||||
</span>
|
||||
{{#if this.showUserStatus}}
|
||||
<UserStatusMessage
|
||||
@status={{get this.users "0.status"}}
|
||||
@showDescription={{if this.site.mobileView "true"}}
|
||||
class="chat-channel__user-status-message"
|
||||
/>
|
||||
{{/if}}
|
||||
<div class="chat-channel-name__label">
|
||||
{{replaceEmoji this.channelTitle}}
|
||||
|
||||
{{#if this.unreadIndicator}}
|
||||
<ChatChannelUnreadIndicator @channel={{@channel}} />
|
||||
{{/if}}
|
||||
|
||||
{{#if this.showUserStatus}}
|
||||
<UserStatusMessage
|
||||
@status={{get this.users "0.status"}}
|
||||
@showDescription={{if this.site.mobileView "true"}}
|
||||
class="chat-channel__user-status-message"
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.showPluginOutlet}}
|
||||
<PluginOutlet
|
||||
@name="after-chat-channel-username"
|
||||
@outletArgs={{hash user=@user}}
|
||||
|
@ -65,15 +86,11 @@ export default class ChatChannelName extends Component {
|
|||
@connectorTagName=""
|
||||
/>
|
||||
{{/if}}
|
||||
{{else if @channel.isCategoryChannel}}
|
||||
<span class="chat-channel-name__label">
|
||||
{{replaceEmoji @channel.title}}
|
||||
</span>
|
||||
|
||||
{{#if (has-block)}}
|
||||
{{yield}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import ChannelIcon from "discourse/plugins/chat/discourse/components/channel-ico
|
|||
import ChannelName from "discourse/plugins/chat/discourse/components/channel-name";
|
||||
|
||||
const ChatChannelTitle = <template>
|
||||
<span
|
||||
<div
|
||||
class={{concatClass
|
||||
"chat-channel-title"
|
||||
(if @channel.isDirectMessageChannel "is-dm" "is-category")
|
||||
|
@ -12,10 +12,14 @@ const ChatChannelTitle = <template>
|
|||
<ChannelIcon @channel={{@channel}} />
|
||||
<ChannelName @channel={{@channel}} />
|
||||
|
||||
{{#if @isUnread}}
|
||||
<div class="unread-indicator {{if @isUrgent '-urgent'}}"></div>
|
||||
{{/if}}
|
||||
|
||||
{{#if (has-block)}}
|
||||
{{yield}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</div>
|
||||
</template>;
|
||||
|
||||
export default ChatChannelTitle;
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import Component from "@glimmer/component";
|
||||
import I18n from "discourse-i18n";
|
||||
import ChatChannelUnreadIndicator from "./chat-channel-unread-indicator";
|
||||
|
||||
export default class ChatChannelMetadata extends Component {
|
||||
get unreadIndicator() {
|
||||
return this.args.unreadIndicator ?? false;
|
||||
}
|
||||
|
||||
get lastMessageFormattedDate() {
|
||||
return moment(this.args.channel.lastMessage.createdAt).calendar(null, {
|
||||
sameDay: "LT",
|
||||
|
@ -23,10 +18,6 @@ export default class ChatChannelMetadata extends Component {
|
|||
{{this.lastMessageFormattedDate}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.unreadIndicator}}
|
||||
<ChatChannelUnreadIndicator @channel={{@channel}} />
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -196,11 +196,8 @@ export default class ChatChannelRow extends Component {
|
|||
>
|
||||
<ChannelIcon @channel={{@channel}} />
|
||||
<div class="chat-channel-row__info">
|
||||
<ChannelName @channel={{@channel}} />
|
||||
<ChatChannelMetadata
|
||||
@channel={{@channel}}
|
||||
@unreadIndicator={{true}}
|
||||
/>
|
||||
<ChannelName @channel={{@channel}} @unreadIndicator={{true}} />
|
||||
<ChatChannelMetadata @channel={{@channel}} />
|
||||
{{#if this.shouldRenderLastMessage}}
|
||||
<div class="chat-channel__last-message">
|
||||
{{replaceEmoji (htmlSafe @channel.lastMessage.excerpt)}}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { service } from "@ember/service";
|
||||
import { gt, not } from "truth-helpers";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import ChannelTitle from "discourse/plugins/chat/discourse/components/channel-title";
|
||||
|
||||
export default class Channel extends Component {
|
||||
|
@ -22,14 +21,11 @@ export default class Channel extends Component {
|
|||
class="chat-message-creator__chatable -category-channel"
|
||||
data-disabled={{not @item.enabled}}
|
||||
>
|
||||
<ChannelTitle @channel={{@item.model}} />
|
||||
|
||||
{{#if (gt @item.tracking.unreadCount 0)}}
|
||||
|
||||
<div
|
||||
class={{concatClass "unread-indicator" (if this.isUrgent "-urgent")}}
|
||||
></div>
|
||||
{{/if}}
|
||||
<ChannelTitle
|
||||
@channel={{@item.model}}
|
||||
@isUnread={{gt @item.tracking.unreadCount 0}}
|
||||
@isUrgent={{this.isUrgent}}
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ export default class ChatChannelsManager extends Service {
|
|||
@service chatStateManager;
|
||||
@service currentUser;
|
||||
@service router;
|
||||
@service site;
|
||||
@service siteSettings;
|
||||
@tracked _cached = new TrackedObject();
|
||||
|
||||
|
@ -130,11 +129,7 @@ export default class ChatChannelsManager extends Service {
|
|||
channel.isCategoryChannel && channel.currentUserMembership.following
|
||||
);
|
||||
|
||||
if (this.site.mobileView) {
|
||||
return this.#sortChannelsByActivity(channels);
|
||||
} else {
|
||||
return channels.sort((a, b) => a?.slug?.localeCompare?.(b?.slug));
|
||||
}
|
||||
return this.#sortChannelsByActivity(channels);
|
||||
}
|
||||
|
||||
@cached
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
}
|
||||
|
||||
&__label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
@ -15,4 +17,21 @@
|
|||
width: 1em;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
.chat-channel-unread-indicator {
|
||||
@include chat-unread-indicator;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-left: 0.75em;
|
||||
|
||||
&.-urgent {
|
||||
width: auto;
|
||||
height: auto;
|
||||
min-width: 0.6em;
|
||||
padding: 0.3em 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,22 +90,6 @@
|
|||
align-items: flex-end;
|
||||
flex-direction: column;
|
||||
margin-left: 0.5em;
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__metadata-date {
|
||||
|
|
|
@ -267,11 +267,14 @@
|
|||
}
|
||||
|
||||
.unread-indicator {
|
||||
margin-left: 0.5rem;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: var(--tertiary);
|
||||
border-radius: 100%;
|
||||
|
||||
&.-urgent {
|
||||
background: var(--success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ RSpec.describe "Channel - Info - Settings page", type: :system do
|
|||
expect(page.find(".c-channel-settings__name")["innerHTML"].strip).to eq(
|
||||
"<script>alert('hello')</script>",
|
||||
)
|
||||
expect(page.find(".chat-channel-name__label")["innerHTML"].strip).to eq(
|
||||
expect(page.find(".chat-channel-name__label")["innerHTML"].strip).to include(
|
||||
"<script>alert('hello')</script>",
|
||||
)
|
||||
end
|
||||
|
|
|
@ -66,4 +66,23 @@ module("Discourse Chat | Component | <ChannelName />", function (hooks) {
|
|||
users.mapBy("username").join(", ")
|
||||
);
|
||||
});
|
||||
|
||||
test("unreadIndicator", async function (assert) {
|
||||
const channel = new ChatFabricators(getOwner(this)).directMessageChannel();
|
||||
channel.tracking.unreadCount = 1;
|
||||
|
||||
let unreadIndicator = true;
|
||||
await render(
|
||||
<template><ChannelName @channel={{channel}} @unreadIndicator={{unreadIndicator}}/></template>
|
||||
);
|
||||
|
||||
assert.true(exists(".chat-channel-unread-indicator"));
|
||||
|
||||
unreadIndicator = false;
|
||||
await render(
|
||||
<template><ChannelName @channel={{channel}} @unreadIndicator={{unreadIndicator}}/></template>
|
||||
);
|
||||
|
||||
assert.false(exists(".chat-channel-unread-indicator"));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,7 +3,6 @@ import { render } from "@ember/test-helpers";
|
|||
import hbs from "htmlbars-inline-precompile";
|
||||
import { module, test } from "qunit";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { exists } from "discourse/tests/helpers/qunit-helpers";
|
||||
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||
|
||||
module("Discourse Chat | Component | chat-channel-metadata", function (hooks) {
|
||||
|
@ -29,23 +28,4 @@ module("Discourse Chat | Component | chat-channel-metadata", function (hooks) {
|
|||
.dom(".chat-channel__metadata-date")
|
||||
.hasText(lastMessageSentAt.format("LT"));
|
||||
});
|
||||
|
||||
test("unreadIndicator", async function (assert) {
|
||||
this.channel = new ChatFabricators(getOwner(this)).directMessageChannel();
|
||||
this.channel.tracking.unreadCount = 1;
|
||||
|
||||
this.unreadIndicator = true;
|
||||
await render(
|
||||
hbs`<ChatChannelMetadata @channel={{this.channel}} @unreadIndicator={{this.unreadIndicator}}/>`
|
||||
);
|
||||
|
||||
assert.true(exists(".chat-channel-unread-indicator"));
|
||||
|
||||
this.unreadIndicator = false;
|
||||
await render(
|
||||
hbs`<ChatChannelMetadata @channel={{this.channel}} @unreadIndicator={{this.unreadIndicator}}/>`
|
||||
);
|
||||
|
||||
assert.false(exists(".chat-channel-unread-indicator"));
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue