Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 911e01e

Browse files
committed
Have pills use solid backgrounds rather than colored images
Similar to room and member avatars, pills now use colored pseudo-elements rather than background images. Signed-off-by: Clark Fischer <[email protected]>
1 parent 759eb4a commit 911e01e

File tree

5 files changed

+148
-20
lines changed

5 files changed

+148
-20
lines changed

res/css/views/rooms/_BasicMessageComposer.pcss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ limitations under the License.
7878
min-width: $font-16px; /* ensure the avatar is not compressed */
7979
height: $font-16px;
8080
margin-inline-end: 0.24rem;
81-
background: var(--avatar-background), $background;
81+
background: var(--avatar-background);
8282
color: $avatar-initial-color;
8383
background-repeat: no-repeat;
8484
background-size: $font-16px;

src/Avatar.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@ export function avatarUrlForMember(
4747
return url;
4848
}
4949

50+
export function getMemberAvatar(
51+
member: RoomMember | null | undefined,
52+
width: number,
53+
height: number,
54+
resizeMethod: ResizeMethod,
55+
): string | undefined {
56+
const mxcUrl = member?.getMxcAvatarUrl();
57+
if (!mxcUrl) return undefined;
58+
return mediaFromMxc(mxcUrl).getThumbnailOfSourceHttp(width, height, resizeMethod);
59+
}
60+
5061
export function avatarUrlForUser(
5162
user: Pick<User, "avatarUrl">,
5263
width: number,

src/editor/parts.ts

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/*
2-
Copyright 2019 New Vector Ltd
3-
Copyright 2019 The Matrix.org Foundation C.I.C.
2+
Copyright 2019, 2023 The Matrix.org Foundation C.I.C.
43
54
Licensed under the Apache License, Version 2.0 (the "License");
65
you may not use this file except in compliance with the License.
@@ -295,8 +294,8 @@ export abstract class PillPart extends BasePart implements IPillPart {
295294
}
296295

297296
// helper method for subclasses
298-
protected setAvatarVars(node: HTMLElement, avatarUrl: string, initialLetter: string | undefined): void {
299-
const avatarBackground = `url('${avatarUrl}')`;
297+
protected setAvatarVars(node: HTMLElement, avatarBackground: string, initialLetter: string | undefined): void {
298+
// const avatarBackground = `url('${avatarUrl}')`;
300299
const avatarLetter = `'${initialLetter || ""}'`;
301300
// check if the value is changing,
302301
// otherwise the avatars flicker on every keystroke while updating.
@@ -413,13 +412,15 @@ class RoomPillPart extends PillPart {
413412
}
414413

415414
protected setAvatar(node: HTMLElement): void {
416-
let initialLetter: string | undefined = "";
417-
let avatarUrl = Avatar.avatarUrlForRoom(this.room, 16, 16, "crop");
418-
if (!avatarUrl) {
419-
initialLetter = Avatar.getInitialLetter(this.room?.name || this.resourceId);
420-
avatarUrl = Avatar.defaultAvatarUrlForString(this.room?.roomId ?? this.resourceId);
415+
const avatarUrl = Avatar.avatarUrlForRoom(this.room, 16, 16, "crop");
416+
if (avatarUrl) {
417+
this.setAvatarVars(node, `url('${avatarUrl}')`, "");
418+
return;
421419
}
422-
this.setAvatarVars(node, avatarUrl, initialLetter);
420+
421+
const initialLetter = Avatar.getInitialLetter(this.room?.name || this.resourceId);
422+
const color = Avatar.getColorForString(this.room?.roomId ?? this.resourceId);
423+
this.setAvatarVars(node, color, initialLetter);
423424
}
424425

425426
public get type(): IPillPart["type"] {
@@ -465,14 +466,17 @@ class UserPillPart extends PillPart {
465466
if (!this.member) {
466467
return;
467468
}
468-
const name = this.member.name || this.member.userId;
469-
const defaultAvatarUrl = Avatar.defaultAvatarUrlForString(this.member.userId);
470-
const avatarUrl = Avatar.avatarUrlForMember(this.member, 16, 16, "crop");
471-
let initialLetter: string | undefined = "";
472-
if (avatarUrl === defaultAvatarUrl) {
473-
initialLetter = Avatar.getInitialLetter(name);
469+
470+
const avatar = Avatar.getMemberAvatar(this.member, 16, 16, "crop");
471+
if (avatar) {
472+
this.setAvatarVars(node, `url('${avatar}')`, "");
473+
return;
474474
}
475-
this.setAvatarVars(node, avatarUrl, initialLetter);
475+
476+
const name = this.member.name || this.member.userId;
477+
const initialLetter = Avatar.getInitialLetter(name);
478+
const color = Avatar.getColorForString(this.member.userId);
479+
this.setAvatarVars(node, color, initialLetter);
476480
}
477481

478482
protected onClick = (): void => {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`RoomPillPart matches snapshot (avatar) 1`] = `
4+
<span
5+
class="mx_Pill mx_RoomPill"
6+
contenteditable="false"
7+
spellcheck="false"
8+
style="--avatar-background: url('http://this.is.a.url/www.example.com/avatars/room1.jpeg'); --avatar-letter: '';"
9+
>
10+
!room:example.com
11+
</span>
12+
`;
13+
14+
exports[`RoomPillPart matches snapshot (no avatar) 1`] = `
15+
<span
16+
class="mx_Pill mx_RoomPill"
17+
contenteditable="false"
18+
spellcheck="false"
19+
style="--avatar-background: #ac3ba8; --avatar-letter: '!';"
20+
>
21+
!room:example.com
22+
</span>
23+
`;
24+
25+
exports[`UserPillPart matches snapshot (avatar) 1`] = `
26+
<span
27+
class="mx_UserPill mx_Pill"
28+
contenteditable="false"
29+
spellcheck="false"
30+
style="--avatar-background: url('http://this.is.a.url/www.example.com/avatar.png'); --avatar-letter: '';"
31+
>
32+
DisplayName
33+
</span>
34+
`;
35+
36+
exports[`UserPillPart matches snapshot (no avatar) 1`] = `
37+
<span
38+
class="mx_UserPill mx_Pill"
39+
contenteditable="false"
40+
spellcheck="false"
41+
style="--avatar-background: #ac3ba8; --avatar-letter: 'U';"
42+
>
43+
DisplayName
44+
</span>
45+
`;

test/editor/parts-test.ts

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2022 The Matrix.org Foundation C.I.C.
2+
Copyright 2022 - 2023 The Matrix.org Foundation C.I.C.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -14,7 +14,11 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import { EmojiPart, PlainPart } from "../../src/editor/parts";
17+
import { MatrixClient, Room, RoomMember } from "matrix-js-sdk/src/matrix";
18+
19+
import { EmojiPart, PartCreator, PlainPart } from "../../src/editor/parts";
20+
import DMRoomMap from "../../src/utils/DMRoomMap";
21+
import { stubClient } from "../test-utils";
1822
import { createPartCreator } from "./mock";
1923

2024
describe("editor/parts", () => {
@@ -40,3 +44,67 @@ describe("editor/parts", () => {
4044
expect(() => part.toDOMNode()).not.toThrow();
4145
});
4246
});
47+
48+
describe("UserPillPart", () => {
49+
const roomId = "!room:example.com";
50+
let client: MatrixClient;
51+
let room: Room;
52+
let creator: PartCreator;
53+
54+
beforeEach(() => {
55+
client = stubClient();
56+
room = new Room(roomId, client, "@me:example.com");
57+
creator = new PartCreator(room, client);
58+
});
59+
60+
it("matches snapshot (no avatar)", () => {
61+
jest.spyOn(room, "getMember").mockReturnValue(new RoomMember(room.roomId, "@user:example.com"));
62+
const pill = creator.userPill("DisplayName", "@user:example.com");
63+
const el = pill.toDOMNode();
64+
65+
expect(el).toMatchSnapshot();
66+
});
67+
68+
it("matches snapshot (avatar)", () => {
69+
const member = new RoomMember(room.roomId, "@user:example.com");
70+
jest.spyOn(room, "getMember").mockReturnValue(member);
71+
jest.spyOn(member, "getMxcAvatarUrl").mockReturnValue("mxc://www.example.com/avatar.png");
72+
73+
const pill = creator.userPill("DisplayName", "@user:example.com");
74+
const el = pill.toDOMNode();
75+
76+
expect(el).toMatchSnapshot();
77+
});
78+
});
79+
80+
describe("RoomPillPart", () => {
81+
const roomId = "!room:example.com";
82+
let client: jest.Mocked<MatrixClient>;
83+
let room: Room;
84+
let creator: PartCreator;
85+
86+
beforeEach(() => {
87+
client = stubClient() as jest.Mocked<MatrixClient>;
88+
DMRoomMap.makeShared();
89+
90+
room = new Room(roomId, client, "@me:example.com");
91+
client.getRoom.mockReturnValue(room);
92+
creator = new PartCreator(room, client);
93+
});
94+
95+
it("matches snapshot (no avatar)", () => {
96+
jest.spyOn(room, "getMxcAvatarUrl").mockReturnValue(null);
97+
const pill = creator.roomPill("super-secret clubhouse");
98+
const el = pill.toDOMNode();
99+
100+
expect(el).toMatchSnapshot();
101+
});
102+
103+
it("matches snapshot (avatar)", () => {
104+
jest.spyOn(room, "getMxcAvatarUrl").mockReturnValue("mxc://www.example.com/avatars/room1.jpeg");
105+
const pill = creator.roomPill("cool chat club");
106+
const el = pill.toDOMNode();
107+
108+
expect(el).toMatchSnapshot();
109+
});
110+
});

0 commit comments

Comments
 (0)