FIX: makes chat user avatar show presence by default ()

It's way more common to have presence enabled than disabled, so we should have been making it the default from start.

This commit also changes the namespace of `<ChatUserAvatar />` into `<Chat::UserAvatar />` and refactors tests.
This commit is contained in:
Joffrey JAFFEUX 2023-07-10 09:36:20 +02:00 committed by GitHub
parent 81a16a105e
commit 03e495186f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 121 additions and 114 deletions

@ -7,7 +7,7 @@
{{@channel.chatable.users.length}}
</span>
{{else}}
<ChatUserAvatar @user={{@channel.chatable.users.firstObject}} />
<Chat::UserAvatar @user={{@channel.chatable.users.firstObject}} />
{{/if}}
</div>
{{/if}}

@ -5,7 +5,7 @@
>
<div class="chat-reply">
{{d-icon (if @message.editing "pencil-alt" "reply")}}
<ChatUserAvatar @user={{@message.user}} />
<Chat::UserAvatar @user={{@message.user}} />
<span class="chat-reply__username">{{@message.user.username}}</span>
<span class="chat-reply__excerpt">
{{replace-emoji @message.excerpt}}

@ -16,7 +16,7 @@
<div class="chat-message-actions">
<div class="selected-message-container">
<div class="selected-message">
<ChatUserAvatar @user={{this.message.user}} />
<Chat::UserAvatar @user={{this.message.user}} />
<span
{{on "touchstart" this.expandReply passive=true}}
role="button"

@ -9,7 +9,7 @@
{{#if @message.inReplyTo.chatWebhookEvent.emoji}}
<ChatEmojiAvatar @emoji={{@message.inReplyTo.chatWebhookEvent.emoji}} />
{{else}}
<ChatUserAvatar @user={{@message.inReplyTo.user}} />
<Chat::UserAvatar @user={{@message.inReplyTo.user}} />
{{/if}}
<span class="chat-reply__excerpt">

@ -10,7 +10,7 @@
>
<div class="chat-message-thread-indicator__last-reply-avatar">
<ChatUserAvatar
<Chat::UserAvatar
@user={{@message.thread.preview.lastReplyUser}}
@avatarSize="small"
/>

@ -1,11 +0,0 @@
<div
class="chat-user-avatar {{if (and this.isOnline @showPresence) 'is-online'}}"
>
<div
role="button"
class="chat-user-avatar-container clickable"
data-user-card={{@user.username}}
>
{{avatar @user imageSize=this.avatarSize}}
</div>
</div>

@ -1,6 +1,6 @@
{{#if @user}}
<a href={{this.userPath}} data-user-card={{@user.username}}>
<ChatUserAvatar @user={{@user}} @avatarSize="medium" />
<Chat::UserAvatar @user={{@user}} @avatarSize="medium" />
</a>
<a href={{this.userPath}} data-user-card={{@user.username}}>
<ChatUserDisplayName @user={{@user}} />

@ -1,4 +1,4 @@
<ChatUserAvatar @user={{@content.model}} @showPresence={{true}} />
<Chat::UserAvatar @user={{@content.model}} />
<ChatUserDisplayName @user={{@content.model}} />
{{#if (gt @content.tracking.unreadCount 0)}}

@ -1,4 +1,4 @@
<ChatUserAvatar @user={{@selection.model}} @showPresence={{true}} />
<Chat::UserAvatar @user={{@selection.model}} />
<span class="chat-message-creator__selection-item__username">
{{@selection.model.username}}

@ -2,10 +2,6 @@
{{#if @message.chatWebhookEvent.emoji}}
<ChatEmojiAvatar @emoji={{@message.chatWebhookEvent.emoji}} />
{{else}}
<ChatUserAvatar
@showPresence={{true}}
@user={{@message.user}}
@avatarSize="medium"
/>
<Chat::UserAvatar @user={{@message.user}} @avatarSize="medium" />
{{/if}}
</div>

@ -14,7 +14,7 @@
>
<div class="chat-thread-list-item__header">
<div class="chat-thread-list-item__om-user-avatar">
<ChatUserAvatar @user={{@thread.originalMessage.user}} />
<Chat::UserAvatar @user={{@thread.originalMessage.user}} />
</div>
<div class="chat-thread-list-item__title overflow-ellipsis">
{{replace-emoji this.title}}

@ -2,7 +2,7 @@
<div class="chat-thread-participants">
<div class="chat-thread-participants__avatar-group">
{{#each @thread.preview.participantUsers as |user|}}
<ChatUserAvatar
<Chat::UserAvatar
@user={{user}}
@avatarSize="tiny"
@showPresence={{false}}

@ -0,0 +1,9 @@
<div class="chat-user-avatar {{if this.isOnline 'is-online'}}">
<div
role="button"
class="chat-user-avatar__container clickable"
data-user-card={{@user.username}}
>
{{avatar @user imageSize=this.avatarSize}}
</div>
</div>

@ -8,12 +8,19 @@ export default class ChatUserAvatar extends Component {
return this.args.avatarSize || "tiny";
}
get showPresence() {
return this.args.showPresence ?? true;
}
get isOnline() {
const users = (this.args.chat || this.chat).presenceChannel?.users;
return (
!!users?.findBy("id", this.args.user?.id) ||
!!users?.findBy("username", this.args.user?.username)
this.showPresence &&
!!users?.find(
({ id, username }) =>
this.args.user?.id === id || this.args.user?.username === username
)
);
}
}

@ -186,60 +186,6 @@ html.ios-device.keyboard-visible body #main-outlet .full-page-chat {
}
}
.chat-user-avatar {
@include unselectable;
display: flex;
align-items: center;
.chat-message-container:not(.has-reply) & {
width: var(--message-left-width);
flex-shrink: 0;
}
&.is-online {
.chat-user-avatar-container .avatar {
box-shadow: 0px 0px 0px 1px var(--success);
border: 1px solid var(--secondary);
padding: 0;
}
}
.chat-user-avatar-container {
position: relative;
padding: 1px; // for is-online box-shadow effect, preventing cutoff
.avatar {
padding: 1px; // for is-online box-shadow effect, preventing shift
}
.chat-user-presence-flair {
box-sizing: border-box;
position: absolute;
background-color: var(--success);
border: 1px solid var(--secondary);
border-radius: 50%;
.chat-message & {
width: 10px;
height: 10px;
right: 0px;
bottom: 0px;
}
.chat-channel-title & {
width: 8px;
height: 8px;
right: -1px;
bottom: -1px;
}
}
}
.chat-channel-title & {
width: auto;
}
}
.topic-title-chat-icon {
display: inline-block;
* {

@ -121,7 +121,7 @@
}
}
.chat-message-avatar .chat-user-avatar .chat-user-avatar-container .avatar,
.chat-message-avatar .chat-user-avatar .chat-user-avatar__container .avatar,
.chat-emoji-avatar .chat-emoji-avatar-container {
width: 28px;
height: 28px;

@ -11,7 +11,7 @@
align-items: center;
justify-content: flex-end;
.chat-user-avatar-container {
.chat-user-avatar__container {
padding: 0;
}
@ -32,6 +32,7 @@
&__avatar-group {
flex-direction: row;
justify-content: flex-start;
.chat-user-avatar {
&:not(:last-child) {
margin-right: -10px;

@ -0,0 +1,53 @@
.chat-user-avatar {
@include unselectable;
display: flex;
align-items: center;
.chat-message-container:not(.has-reply) & {
width: var(--message-left-width);
flex-shrink: 0;
}
&.is-online {
.chat-user-avatar__container .avatar {
box-shadow: 0px 0px 0px 1px var(--success);
border: 1px solid var(--secondary);
padding: 0;
}
}
&__container {
position: relative;
padding: 1px; // for is-online box-shadow effect, preventing cutoff
.avatar {
padding: 1px; // for is-online box-shadow effect, preventing shift
}
.chat-user-presence-flair {
box-sizing: border-box;
position: absolute;
background-color: var(--success);
border: 1px solid var(--secondary);
border-radius: 50%;
.chat-message & {
width: 10px;
height: 10px;
right: 0px;
bottom: 0px;
}
.chat-channel-title & {
width: 8px;
height: 8px;
right: -1px;
bottom: -1px;
}
}
}
.chat-channel-title & {
width: auto;
}
}

@ -59,3 +59,4 @@
@import "chat-message-error";
@import "chat-new-message-modal";
@import "chat-message-creator";
@import "chat-user-avatar";

@ -33,7 +33,7 @@ module PageObjects
def has_participant?(user)
find(@context).has_css?(
".chat-thread-participants__avatar-group .chat-user-avatar .chat-user-avatar-container[data-user-card=\"#{user.username}\"] img",
".chat-thread-participants__avatar-group .chat-user-avatar .chat-user-avatar__container[data-user-card=\"#{user.username}\"] img",
)
end

@ -32,7 +32,7 @@ module PageObjects
end
def avatar_selector(user)
".chat-thread-list-item__om-user-avatar .chat-user-avatar .chat-user-avatar-container[data-user-card=\"#{user.username}\"] img"
".chat-thread-list-item__om-user-avatar .chat-user-avatar .chat-user-avatar__container[data-user-card=\"#{user.username}\"] img"
end
def last_reply_datetime_selector(last_reply)

@ -71,7 +71,7 @@ module("Discourse Chat | Component | chat-channel-title", function (hooks) {
const user = this.channel.chatable.users[0];
assert.true(
exists(`.chat-user-avatar-container .avatar[title="${user.username}"]`)
exists(`.chat-user-avatar__container .avatar[title="${user.username}"]`)
);
assert.strictEqual(

@ -1,50 +1,55 @@
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { exists } from "discourse/tests/helpers/qunit-helpers";
import hbs from "htmlbars-inline-precompile";
import { module, test } from "qunit";
import { render } from "@ember/test-helpers";
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
const user = {
id: 1,
username: "markvanlan",
name: null,
avatar_template: "/letter_avatar_proxy/v4/letter/m/48db29/{size}.png",
};
function containerSelector(user, options = {}) {
let onlineSelector = ":not(.is-online)";
if (options.online) {
onlineSelector = ".is-online";
}
module("Discourse Chat | Component | chat-user-avatar", function (hooks) {
return `.chat-user-avatar${onlineSelector} .chat-user-avatar__container[data-user-card=${user.username}] .avatar[title=${user.username}]`;
}
module("Discourse Chat | Component | <Chat::UserAvatar />", function (hooks) {
setupRenderingTest(hooks);
test("user is not online", async function (assert) {
this.set("user", user);
this.set("chat", { presenceChannel: { users: [] } });
this.user = fabricators.user();
this.chat = { presenceChannel: { users: [] } };
await render(
hbs`<ChatUserAvatar @chat={{this.chat}} @user={{this.user}} />`
hbs`<Chat::UserAvatar @chat={{this.chat}} @user={{this.user}} />`
);
assert.true(
exists(
`.chat-user-avatar .chat-user-avatar-container[data-user-card=${user.username}] .avatar[title=${user.username}]`
)
);
assert.false(exists(".chat-user-avatar.is-online"));
assert.dom(containerSelector(this.user, { online: false })).exists();
});
test("user is online", async function (assert) {
this.set("user", user);
this.set("chat", {
presenceChannel: { users: [{ id: user.id }] },
});
this.user = fabricators.user();
this.chat = {
presenceChannel: { users: [{ id: this.user.id }] },
};
await render(
hbs`<ChatUserAvatar @showPresence={{true}} @chat={{this.chat}} @user={{this.user}} />`
hbs`<Chat::UserAvatar @chat={{this.chat}} @user={{this.user}} />`
);
assert.true(
exists(
`.chat-user-avatar .chat-user-avatar-container[data-user-card=${user.username}] .avatar[title=${user.username}]`
)
assert.dom(containerSelector(this.user, { online: true })).exists();
});
test("showPresence=false", async function (assert) {
this.user = fabricators.user();
this.chat = {
presenceChannel: { users: [{ id: this.user.id }] },
};
await render(
hbs`<Chat::UserAvatar @showPresence={{false}} @chat={{this.chat}} @user={{this.user}} />`
);
assert.true(exists(".chat-user-avatar.is-online"));
assert.dom(containerSelector(this.user, { online: false })).exists();
});
});