Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions playwright/e2e/composer/CIDER.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
test.describe("Composer", () => {
test.use({
displayName: "Janet",
botCreateOpts: {
displayName: "Bob",
},
});

test.use({
Expand Down Expand Up @@ -94,5 +97,22 @@
).toBeVisible();
});
});

test("can send mention", { tag: "@screenshot" }, async ({ page, bot, app }) => {
// Set up a private room so we have another user to mention
await app.client.createRoom({
is_direct: true,
invite: [bot.credentials.userId],
});
await app.viewRoomByName("Bob");

const composer = page.getByRole("textbox", { name: "Send an unencrypted message…" });
await composer.pressSequentially("@bob");
await page.getByRole("option", { name: "Bob" }).click();
await expect(composer.getByText("Bob")).toBeVisible();
await expect(composer).toMatchScreenshot("mention.png");

Check failure on line 113 in playwright/e2e/composer/CIDER.spec.ts

View workflow job for this annotation

GitHub Actions / Run Tests [Chrome] 1/6

[Chrome] › playwright/e2e/composer/CIDER.spec.ts:101:13 › Composer › CIDER › can send mention @screenshot

1) [Chrome] › playwright/e2e/composer/CIDER.spec.ts:101:13 › Composer › CIDER › can send mention @screenshot Error: expect(locator).toHaveScreenshot(expected) 13 pixels (ratio 0.01 of all image pixels) are different. Snapshot: mention.png Call log: - Expect "toHaveScreenshot(mention.png)" with timeout 5000ms - verifying given screenshot expectation - waiting for getByRole('textbox', { name: 'Send an unencrypted message…' }) - locator resolved to <div dir="auto" tabindex="0" role="textbox" translate="no" aria-multiline="true" aria-disabled="false" contenteditable="true" aria-haspopup="listbox" aria-autocomplete="list" data-testid="basicmessagecomposer" aria-label="Send an unencrypted message…" class="mx_BasicMessageComposer_input mx_BasicMessageComposer_input_shouldShowPillAvatar">…</div> - taking element screenshot - disabled all CSS animations - waiting for fonts to load... - fonts loaded - attempting scroll into view action - waiting for element to be stable - 13 pixels (ratio 0.01 of all image pixels) are different. - waiting 100ms before taking screenshot - waiting for getByRole('textbox', { name: 'Send an unencrypted message…' }) - locator resolved to <div dir="auto" tabindex="0" role="textbox" translate="no" aria-multiline="true" aria-disabled="false" contenteditable="true" aria-haspopup="listbox" aria-autocomplete="list" data-testid="basicmessagecomposer" aria-label="Send an unencrypted message…" class="mx_BasicMessageComposer_input mx_BasicMessageComposer_input_shouldShowPillAvatar">…</div> - taking element screenshot - disabled all CSS animations - waiting for fonts to load... - fonts loaded - attempting scroll into view action - waiting for element to be stable - captured a stable screenshot - 13 pixels (ratio 0.01 of all image pixels) are different. 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 | }); at /home/runner/work/element-web/element-web/playwright/e2e/composer/CIDER.spec.ts:113:36
await composer.press("Enter");
await expect(page.locator(".mx_EventTile_body", { hasText: "Bob" })).toBeVisible();
});
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions res/css/views/rooms/_BasicMessageComposer.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Please see LICENSE files in the repository root for full details.
/* These are set in Javascript */
--avatar-letter: "";
--avatar-background: unset;
--avatar-color: unset;
--placeholder: "";

position: relative;
Expand Down Expand Up @@ -54,6 +55,8 @@ Please see LICENSE files in the repository root for full details.
span.mx_UserPill,
span.mx_RoomPill,
span.mx_SpacePill {
display: inline-flex;
align-items: center;
user-select: all;
position: relative;
cursor: unset; /* We don't want indicate clickability */
Expand Down
23 changes: 19 additions & 4 deletions src/Avatar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ import DMRoomMap from "./utils/DMRoomMap";
import { mediaFromMxc } from "./customisations/Media";
import { isLocalRoom } from "./utils/localRoom/isLocalRoom";
import { getFirstGrapheme } from "./utils/strings";
import ThemeWatcher from "./settings/watchers/ThemeWatcher";

/**
* Hardcoded from the Compound colors.
* Shade for background as defined in the compound web implementation
* https://github.com/vector-im/compound-web/blob/main/src/components/Avatar
*/
const AVATAR_BG_COLORS = ["#e9f2ff", "#faeefb", "#e3f7ed", "#ffecf0", "#ffefe4", "#e3f5f8", "#f1efff", "#e0f8d9"];
const AVATAR_TEXT_COLORS = ["#043894", "#671481", "#004933", "#7e0642", "#850000", "#004077", "#4c05b5", "#004b00"];
const AVATAR_BG_LIGTH_COLORS = ["#e0f8d9", "#e3f5f8", "#faeefb", "#f1efff", "#ffecf0", "#ffefe4"];
const AVATAR_TEXT_LIGHT_COLORS = ["#005f00", "#00548c", "#822198", "#5d26cd", "#9f0850", "#9b2200"];

const AVATAR_BG_DARK_COLORS = ["#002600", "#001b4e", "#37004e", "#22006a", "#450018", "#470000"];
const AVATAR_TEXT_DARK_COLORS = ["#56c02c", "#21bacd", "#d991de", "#ad9cfe", "#fe84a2", "#f6913d"];

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

/**
* Determines if the current theme is dark
*/
function isDarkTheme(): boolean {
return new ThemeWatcher().getEffectiveTheme() === "dark";
}

/**
* Determines the HEX color to use in the avatar pills
* @param id the user or room ID
Expand All @@ -51,7 +62,8 @@ export function getAvatarTextColor(id: string): string {
// eslint-disable-next-line react-hooks/rules-of-hooks
const index = useIdColorHash(id);

return AVATAR_TEXT_COLORS[index - 1];
// Use light colors by default
return isDarkTheme() ? AVATAR_TEXT_DARK_COLORS[index - 1] : AVATAR_TEXT_LIGHT_COLORS[index - 1];
}

export function avatarUrlForUser(
Expand Down Expand Up @@ -103,7 +115,10 @@ export function defaultAvatarUrlForString(s: string): string {
// overwritten color value in custom themes
const cssVariable = `--avatar-background-colors_${colorIndex}`;
const cssValue = getComputedStyle(document.body).getPropertyValue(cssVariable);
const color = cssValue || AVATAR_BG_COLORS[colorIndex - 1];
// Light colors are the default
const color =
cssValue || isDarkTheme() ? AVATAR_BG_DARK_COLORS[colorIndex - 1] : AVATAR_BG_LIGTH_COLORS[colorIndex - 1];

let dataUrl = colorToDataURLCache.get(color);
if (!dataUrl) {
// validate color as this can come from account_data
Expand Down
18 changes: 15 additions & 3 deletions src/editor/parts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,12 @@ export abstract class PillPart extends BasePart implements IPillPart {
}

// helper method for subclasses
protected setAvatarVars(node: HTMLElement, avatarUrl: string, initialLetter: string): void {
protected setAvatarVars(
node: HTMLElement,
avatarUrl: string,
initialLetter: string,
avatarTextColor?: string,
): void {
const avatarBackground = `url('${avatarUrl}')`;
const avatarLetter = `'${initialLetter}'`;
// check if the value is changing,
Expand All @@ -308,6 +313,9 @@ export abstract class PillPart extends BasePart implements IPillPart {
if (node.style.getPropertyValue("--avatar-letter") !== avatarLetter) {
node.style.setProperty("--avatar-letter", avatarLetter);
}
if (avatarTextColor && node.style.getPropertyValue("--avatar-color") !== avatarTextColor) {
node.style.setProperty("--avatar-color", avatarTextColor);
}
}

public serialize(): ISerializedPillPart {
Expand Down Expand Up @@ -421,11 +429,13 @@ class RoomPillPart extends PillPart {
protected setAvatar(node: HTMLElement): void {
let initialLetter = "";
let avatarUrl = Avatar.avatarUrlForRoom(this.room ?? null, 16, 16, "crop");
let avatarTextColor: string | undefined;
if (!avatarUrl) {
initialLetter = Avatar.getInitialLetter(this.room?.name || this.resourceId) ?? "";
avatarUrl = Avatar.defaultAvatarUrlForString(this.room?.roomId ?? this.resourceId);
avatarTextColor = Avatar.getAvatarTextColor(this.room?.roomId ?? this.resourceId);
}
this.setAvatarVars(node, avatarUrl, initialLetter);
this.setAvatarVars(node, avatarUrl, initialLetter, avatarTextColor);
}

public get type(): IPillPart["type"] {
Expand Down Expand Up @@ -479,10 +489,12 @@ class UserPillPart extends PillPart {
const defaultAvatarUrl = Avatar.defaultAvatarUrlForString(this.member.userId);
const avatarUrl = Avatar.avatarUrlForMember(this.member, 16, 16, "crop");
let initialLetter = "";
let avatarTextColor: string | undefined;
if (avatarUrl === defaultAvatarUrl) {
initialLetter = Avatar.getInitialLetter(name) ?? "";
avatarTextColor = Avatar.getAvatarTextColor(this.member.userId);
}
this.setAvatarVars(node, avatarUrl, initialLetter);
this.setAvatarVars(node, avatarUrl, initialLetter, avatarTextColor);
}

protected onClick = (): void => {
Expand Down
Loading