Skip to content

Commit d408097

Browse files
floriandurosHalf-Shot
authored andcommitted
Align default avatar and fix colors in composer pills (#30739)
* fix: align default avatar in composer pills * fix: use correct color for avatar in composer pills when there is no image * test(e2e): add test for cider mention * chore: fix typo
1 parent c518606 commit d408097

File tree

5 files changed

+57
-7
lines changed

5 files changed

+57
-7
lines changed

playwright/e2e/composer/CIDER.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ const CtrlOrMeta = process.platform === "darwin" ? "Meta" : "Control";
1414
test.describe("Composer", () => {
1515
test.use({
1616
displayName: "Janet",
17+
botCreateOpts: {
18+
displayName: "Bob",
19+
},
1720
});
1821

1922
test.use({
@@ -94,5 +97,22 @@ test.describe("Composer", () => {
9497
).toBeVisible();
9598
});
9699
});
100+
101+
test("can send mention", { tag: "@screenshot" }, async ({ page, bot, app }) => {
102+
// Set up a private room so we have another user to mention
103+
await app.client.createRoom({
104+
is_direct: true,
105+
invite: [bot.credentials.userId],
106+
});
107+
await app.viewRoomByName("Bob");
108+
109+
const composer = page.getByRole("textbox", { name: "Send an unencrypted message…" });
110+
await composer.pressSequentially("@bob");
111+
await page.getByRole("option", { name: "Bob" }).click();
112+
await expect(composer.getByText("Bob")).toBeVisible();
113+
await expect(composer).toMatchScreenshot("mention.png");
114+
await composer.press("Enter");
115+
await expect(page.locator(".mx_EventTile_body", { hasText: "Bob" })).toBeVisible();
116+
});
97117
});
98118
});
1.58 KB
Loading

res/css/views/rooms/_BasicMessageComposer.pcss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Please see LICENSE files in the repository root for full details.
1010
/* These are set in Javascript */
1111
--avatar-letter: "";
1212
--avatar-background: unset;
13+
--avatar-color: unset;
1314
--placeholder: "";
1415

1516
position: relative;
@@ -54,6 +55,8 @@ Please see LICENSE files in the repository root for full details.
5455
span.mx_UserPill,
5556
span.mx_RoomPill,
5657
span.mx_SpacePill {
58+
display: inline-flex;
59+
align-items: center;
5760
user-select: all;
5861
position: relative;
5962
cursor: unset; /* We don't want indicate clickability */

src/Avatar.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,18 @@ import DMRoomMap from "./utils/DMRoomMap";
1313
import { mediaFromMxc } from "./customisations/Media";
1414
import { isLocalRoom } from "./utils/localRoom/isLocalRoom";
1515
import { getFirstGrapheme } from "./utils/strings";
16+
import ThemeWatcher from "./settings/watchers/ThemeWatcher";
1617

1718
/**
1819
* Hardcoded from the Compound colors.
1920
* Shade for background as defined in the compound web implementation
2021
* https://github.com/vector-im/compound-web/blob/main/src/components/Avatar
2122
*/
22-
const AVATAR_BG_COLORS = ["#e9f2ff", "#faeefb", "#e3f7ed", "#ffecf0", "#ffefe4", "#e3f5f8", "#f1efff", "#e0f8d9"];
23-
const AVATAR_TEXT_COLORS = ["#043894", "#671481", "#004933", "#7e0642", "#850000", "#004077", "#4c05b5", "#004b00"];
23+
const AVATAR_BG_LIGHT_COLORS = ["#e0f8d9", "#e3f5f8", "#faeefb", "#f1efff", "#ffecf0", "#ffefe4"];
24+
const AVATAR_TEXT_LIGHT_COLORS = ["#005f00", "#00548c", "#822198", "#5d26cd", "#9f0850", "#9b2200"];
25+
26+
const AVATAR_BG_DARK_COLORS = ["#002600", "#001b4e", "#37004e", "#22006a", "#450018", "#470000"];
27+
const AVATAR_TEXT_DARK_COLORS = ["#56c02c", "#21bacd", "#d991de", "#ad9cfe", "#fe84a2", "#f6913d"];
2428

2529
// Not to be used for BaseAvatar urls as that has similar default avatar fallback already
2630
export function avatarUrlForMember(
@@ -42,6 +46,13 @@ export function avatarUrlForMember(
4246
return url;
4347
}
4448

49+
/**
50+
* Determines if the current theme is dark
51+
*/
52+
function isDarkTheme(): boolean {
53+
return new ThemeWatcher().getEffectiveTheme() === "dark";
54+
}
55+
4556
/**
4657
* Determines the HEX color to use in the avatar pills
4758
* @param id the user or room ID
@@ -51,7 +62,8 @@ export function getAvatarTextColor(id: string): string {
5162
// eslint-disable-next-line react-hooks/rules-of-hooks
5263
const index = useIdColorHash(id);
5364

54-
return AVATAR_TEXT_COLORS[index - 1];
65+
// Use light colors by default
66+
return isDarkTheme() ? AVATAR_TEXT_DARK_COLORS[index - 1] : AVATAR_TEXT_LIGHT_COLORS[index - 1];
5567
}
5668

5769
export function avatarUrlForUser(
@@ -103,7 +115,10 @@ export function defaultAvatarUrlForString(s: string): string {
103115
// overwritten color value in custom themes
104116
const cssVariable = `--avatar-background-colors_${colorIndex}`;
105117
const cssValue = getComputedStyle(document.body).getPropertyValue(cssVariable);
106-
const color = cssValue || AVATAR_BG_COLORS[colorIndex - 1];
118+
// Light colors are the default
119+
const color =
120+
cssValue || isDarkTheme() ? AVATAR_BG_DARK_COLORS[colorIndex - 1] : AVATAR_BG_LIGHT_COLORS[colorIndex - 1];
121+
107122
let dataUrl = colorToDataURLCache.get(color);
108123
if (!dataUrl) {
109124
// validate color as this can come from account_data

src/editor/parts.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,12 @@ export abstract class PillPart extends BasePart implements IPillPart {
297297
}
298298

299299
// helper method for subclasses
300-
protected setAvatarVars(node: HTMLElement, avatarUrl: string, initialLetter: string): void {
300+
protected setAvatarVars(
301+
node: HTMLElement,
302+
avatarUrl: string,
303+
initialLetter: string,
304+
avatarTextColor?: string,
305+
): void {
301306
const avatarBackground = `url('${avatarUrl}')`;
302307
const avatarLetter = `'${initialLetter}'`;
303308
// check if the value is changing,
@@ -308,6 +313,9 @@ export abstract class PillPart extends BasePart implements IPillPart {
308313
if (node.style.getPropertyValue("--avatar-letter") !== avatarLetter) {
309314
node.style.setProperty("--avatar-letter", avatarLetter);
310315
}
316+
if (avatarTextColor && node.style.getPropertyValue("--avatar-color") !== avatarTextColor) {
317+
node.style.setProperty("--avatar-color", avatarTextColor);
318+
}
311319
}
312320

313321
public serialize(): ISerializedPillPart {
@@ -421,11 +429,13 @@ class RoomPillPart extends PillPart {
421429
protected setAvatar(node: HTMLElement): void {
422430
let initialLetter = "";
423431
let avatarUrl = Avatar.avatarUrlForRoom(this.room ?? null, 16, 16, "crop");
432+
let avatarTextColor: string | undefined;
424433
if (!avatarUrl) {
425434
initialLetter = Avatar.getInitialLetter(this.room?.name || this.resourceId) ?? "";
426435
avatarUrl = Avatar.defaultAvatarUrlForString(this.room?.roomId ?? this.resourceId);
436+
avatarTextColor = Avatar.getAvatarTextColor(this.room?.roomId ?? this.resourceId);
427437
}
428-
this.setAvatarVars(node, avatarUrl, initialLetter);
438+
this.setAvatarVars(node, avatarUrl, initialLetter, avatarTextColor);
429439
}
430440

431441
public get type(): IPillPart["type"] {
@@ -479,10 +489,12 @@ class UserPillPart extends PillPart {
479489
const defaultAvatarUrl = Avatar.defaultAvatarUrlForString(this.member.userId);
480490
const avatarUrl = Avatar.avatarUrlForMember(this.member, 16, 16, "crop");
481491
let initialLetter = "";
492+
let avatarTextColor: string | undefined;
482493
if (avatarUrl === defaultAvatarUrl) {
483494
initialLetter = Avatar.getInitialLetter(name) ?? "";
495+
avatarTextColor = Avatar.getAvatarTextColor(this.member.userId);
484496
}
485-
this.setAvatarVars(node, avatarUrl, initialLetter);
497+
this.setAvatarVars(node, avatarUrl, initialLetter, avatarTextColor);
486498
}
487499

488500
protected onClick = (): void => {

0 commit comments

Comments
 (0)